티스토리 뷰

1. 사람을 사랑한 기술

컴퓨터 프로그래밍 언어에 있어서 발전 과정을 보여준다.

기계어에서 어셈블리어, c언어, 그리고 객체지향 개념이 등장한 c++, 그리고 자바로의 발전에 있어서 주요한 변화에 대해서 설명한다.

기계어와 일상 용어를 일대일로 대응 매칭하는 어셈블리와 각 CPU 마다 다르게 사용했던 어셈블리어에서, 하나의 소스코드를 작성해 CPU에 맞는 컴파일러로 읽어 운영체제용 기계어를 생성하는 C언어, 그리고 C++로의 객체지향의 등장. 그리고 자바의 JRE 설치를 통한 동일한 컴파일러와 단 하나의 JVM용 기계어를 생성하는 것 까지의 발전 과정을 설명해주고 있다.

 

2. 자바의 절차적 / 구조적 프로그래밍

자바는 JVM이라는 가상 기계 위에서 구동된다. 운영체제 역할을 하는 JRE가 그 JVM이라는 가상 컴퓨터를 제어하고, 그 JRE라는 환경에서 사용하는 JDK라는 도구를 사용한다. JDK는 컴파일러인 javac.exe를 포함하고 있고, JRE는 자바 프로그램 실행기인 java.exe를 포함하고 있다.

이러한 가상 환경을 통해서 자바는 실행이 되며, 메모리 사용방식에 대해서 나온다.

Static, Stack, Heap영역을 나누고, main 메소드가 호출되는 과정에 대해서 설명한다.

1. java.lang 패키지를 static에 올리고, 2. 모든 클래스와 임포트 패키지를 static 영역에 올리고, 3. main 메소드 Stack 프레임이 stack 영역에 생기고, 4. args 변수 공간이 그 main 메소드  stack 프레임 안에 생기고 5. main 메소드 안의 내용이 실행되며 sout를 통해 콘솔에 출력하는 것은 GPU에 화면 출력을 의뢰하게 된다고 한다. 6. main 메소드의 끝인 중괄호를 만나면 stack 프레임이 소멸되고, 7. JRE는 JVM을 종료하고 JRE 자체도 운영체제 상의 메모리 상에서 사라진다. (if, for문도 중괄호로 되어있어 stack 프레임을 stack에 생성한다)

변수의 종류인 지역변수와 클래스 멤버변수, 객체 맴버변수의 차이점은 각각 stack, static, heap 영역에 다르게 존재하여 다른 목적을 가진다는 것도 명확히 알게 되었다.

stack 프레임이 서로 다른 변수는 접근 할 수 없으며 클래스 맴버 변수인 전역변수를 사용할때의 위험성에 대해서도 알수 있었다.

멀티쓰레드와 멀티 프로세스의 메모리 모델에 대해서도 차이점을 알 수 있었고 전역변수보다는 지역변수를 써라, 함수를 써라와 같은 절차적 / 구조적 프로그래밍의 유산을 객체 지향 언어인 자바에서 확인할 수 있었다.

 

* 수정) 추가 : 조금더 구체적으로 (2022, 09. 21)

public static void main(String[] args){ }가 클래스 안에서 시작될때 다음과같은 일이 일어난다.

1. JRE는 프로그램에 main() 메소드가 있는지 확인해 있으면 실행을 하기 위해서 JVM을 부팅한다.

2. JVM은 먼저 java.lang 패키지를 static 영역에 올린다. 

3. 또한 모든 클래스, import package를 static 영역에 올린다.

// 여기까지가 main 메소드 실행을 위한 준비인 것이다.

4. main() 메소드의 '{' 여는 중괄호를 만나면 main 메소드에 대한 stack frame이 stack 영역에 할당된다

5. args 변수를 저장할 수 있는 공간이 stack frame 가장 밑에 확보된다.

6. '}' 닫는 괄호로 인해 main() 메소드 stack frame이 소멸된다.

7. main() 메소드가 끝이나고 JRE는 JVM을 종료한다. 그리고 JRE 자체도 운영체제상 메모리에서 사라진다.

 

변수에는 다음 세 가지가 있다.

(1) 지역변수

(2) 클래스 멤버변수

(3) 객체 멤버변수

 

stack frame 외부 에서는 내부 stack frame 접근이 불가능하지만 내부에서는 외부가 접근 가능하다.

** 하지만 메소드 지역변수에서 다른 메소드의 지역변수는 절대 접근 할 수 없다.

public class Start4 {
    public static void main(String[] args) {
        int k = 5;
        int m;

        m = square(k);
    }

    private static int square(int k) {
        int result;
        k = 25;
        result = k;
        return result;
    }
}

위의 코드에서, main()메소드가 가진 변수 k와 square() 메소드가 가진 변수 k가 이름만 같지 실제로는 서로 별도의 변수 공간이라는 것이다. 이것을 Call By Value 라고 한다.

그래서 square() 메소드 안에서의 k 변수에 무슨짓을 해도 main() 메소드 안의 k 변수는 영향을 줄 수 없다.

m = square(k)가 실행되고 있는 동안, square() 메소드 내에서는 main() 메소드의 지역변수를 참조할 수 있을것 같지만, 금지시켜 놓았다. 메소드는 고유의 공간이고 침범되면 문제가 될수 있기 때문이다.

 

하지만 위와 반대로 서로다른 메소드에서 접근 가능한 것이 있는데 그것이 전역변수이다.

static 으로 클래스안에서 변수를 설정해 놓으면, stack frame 밖인 static 영역에 있으니 어디서나 접근 가능하다.

public class Start4 {
    static int share;
    public static void main(String[] args) {
        System.out.println(share);	// 0
        share = 55;
        System.out.println(share);	// 55
        int k = 5;
        int m = sqaure(k);
        System.out.println(share);	// 25
        ...
    }
    private static int square(int k) {
     k = 25;
     share = k;
     result = k;
    }

위 main() 메소드가 실행 되기 전에, share 변수는 static 영역에 변수 공간이 할당되고 0으로 초기화 된다.

그리고 결과는 위와 같다

 

지역변수의 특징 : stack frame에 종속적

전역변수의 특징 : stack frame에 독립적 (어느곳에서나 접근 가능) (static으로 선언한 변수)

하지만 전역변수는 쓰지 말자

프로젝트 규모에 따라 코드가 커지면서 여러 메소드에서 전역 변수의 값을 변경하기 시작하면 메모리로 추적하지 않는 이상 전역변수에 저장되어 있는 값을 파악하기 쉽지 않기 때문이다.

따라서 전역변수는 피할수 있다면 피하기. 

다만 final을 통해서 Math.PI 처럼 읽기 전용 값을 공유해서 전역 상수로 사용하는것은 적극 추천함.

 

 

최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday