JAVA 언어와 Spring Boot FrameWork를 사용한지 2년이 다되갈 무렵 문득 내부 원리에 대한 기억이 희미해져간다...
컴파일 원리와 JVM의 내부 저장소와 동작 원리를 다시 한번 학습하고 넘어가자 !
우선 컴파일 원리를 위한 기본 지식으로
.java 원시코드 (java)
.class 바이트코드 (JVM이 이해하는 언어 | OS에 영향 받지 않는다.)
.jar 바이너리코드(기계어) ( CPU가 인식하는 코드이다.)
자바 코드의 3가지 상태를 정리하고 가자
바이트 코드란 → 가상 컴퓨터(VM) 에서 돌아가는 실행 프로그램을 위한 이진 표현법 !
즉 JVM이 인식하는 코드이다.
바이너리 코드 → 이진 코드이나 CPU가 인식하는 코드이다.
CPU 제조 회사마다 해석되는 코드가 조금씩 상이하다.

.java 파일을 .class 파일로 변환
JDK 설치시 bin에 존재하는 javac.exe이 Compiler이다.
javac test.java | 해당 명령어를 통해서 .java 파일을 .class 파일로 컴파일 한다.
.class 파일을 Load 한다
JDK bin에 존재하는 java.exe는 JVM을 구동시키는 명령 프로그램
java test | 해당 명령어를 통해서 .class 파일을 실행 시킬 수 있다.
즉 JDK를 설치하면 컴파일러와 JVM이 모두 존재한다
JDK를 설치하면 두개의 exe을 확인할 수 있고 IDE를 활용해 컴파일하는 과정을 해당 kit을 이용하여 진행했던 것이다.
JIT 컴파일러(just-in-time compilation)
동적 번역이라고 한다.
프로그램을 실제 실행하는 시점에 기계어로 번역하는 컴파일러 이다.
인터프리터 방식
인터프리터는 적절한 시점에 바이트 코드 전체를 바이너리로 바꿔놓기 때문에 이후에 인터프리팅 하지 않는다.
JAVA 언어는 인터프리터 언어이고 JIT를 통해서 동적으로 변경되는 코드를 감지하고 반영한다.


Runtime Data Area (위 사각형 공간)
프로그램을 수행하기 위해 OS에서 할당받은 메모리 공간
Thread
Thread가 시작될 때 생성되며 생성 될때마다 만들어지는 공간
스레드 별로 하나씩 존재한다.
결국 Java는 하나의 프로세스의 개념이 되는것
프로그램 실행과정에서 임시로 할당되었다가 메소드를 빠져나가면 바로 소멸되는 특성의 데이터를 저장하기 위함
각종 형태의 변수나 임시 데이터, 스레드나 메소드의 정보를 저장
자바 프로그램이 컴파일되어 생성된 기계어(바이너리 코드)로 작성된 프로그램을 실행 시키는 영역
Method Area (class area , static area)
클래스 정보를 처음 메모리에 올릴때 초기화 되는 대상을 저장해준다
스태틱 영역에 존재하는 별도의 관리 영역
상수 자료형을 저장하여 참조하고 중복을 막는 역할

객체를 저장하는 가상메모리 공간.
new 연산자로 생성되는 객체와 배열을 저장한다.
이때 위의 Method Area에 정의된 class만 객체로 생성이 가능하다
영구적인 세대
생성된 객체들의 정보의 주소값이 저장된 공간
클래스 로더에 의해서 저장된(Method Area)에 있는 class, Method 등 Meta 정보(래퍼런스)를 저장해두는 공간이다.
포인터, 래퍼런스를 사용하는 방법은 Reflection 기법이라고 한다.
젊은 영역
추후 가비지 콜렉터에 의해 사라지는 영역이다.
new 연산자로 인해서 생성되는 객체들을 저장해놓고 사용한다.
New/Young 영역에서 발생하는 GC를 “Minor GC” 라고 한다.
추후 가비지 콜렉터에 의해 사라지는 영역이나, 생명주기가 긴 “오래된 객체“를 가지고 있다.
젊은 영역과 같이 생성되는 객체들을 저장해둔다.
Old 영역에서 발생하는 GC를 “Major GC” 라고 한다.
JDK
Java Development Kit
JRE 를 포함하고 있는 Kit로
JAVA를 사용하기 위한 모든 기능을 갖춘 SDK이다.
JRE
Java Runtime Enviroment
JVM + 자바 클래스 라이브러리로 구성 되어있으며,
컴파일된 Java 프로그램을 실행하는데 필요한 패키지이다.
저장소에 대해서 학습하다 보니 문득 클래스 변수의 생명주기와 변수의 동시성 관리에 대해서 궁금해졌다.
그럼 예제를 통해서 직접 테스트 해보자
public class SharedObjectExample {
private int counter = 0; // 객체가 생성될 때 초기화
public void increment() {
counter++;
}
public int getCounter() {
return counter;
}
public static void main(String[] args) {
SharedObjectExample obj = new SharedObjectExample();
// 스레드 1: counter를 증가시키는 작업
Thread incrementThread = new Thread(() -> {
for (int i = 0; i < 5; i++) {
obj.increment();
}
});
// 스레드 2: counter를 0으로 초기화
Thread resetThread = new Thread(() -> {
obj.counter = 0;
});
// 실행
incrementThread.start();
resetThread.start();
try {
incrementThread.join();
resetThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 최종 카운터 값 출력
System.out.println("Final counter value: " + obj.getCounter());
}
}
두개의 스레드를 순차적으로 실행시켰고
1번 스레드의 결과는 5
2번 스레드의 결과는 0이다.
이처럼 자바는 하나의 객체에 변수를 공유 자원으로 사용한다.
이런 자원을 동시에 접근할때 발생하는 문제가, 동시성 문제이다...
여기서 두가지 의문이 생긴다
그러면 for문에서 공통적으로 사용하는 for(int i=0...)
변수 i도 공유하나?
그러면 static 변수를 왜 사용하나?
1번의 대답은 No 이다. class 레벨의 변수들만 객체와 함께 살아있고 메소드의 변수들은 로컬 변수로 스레드에서 항당받은 공간에 존재하기에 스레드간 이동이 불가하기 때문이다.
2번의 대답은 Spring을 사용하기에 보통 컨텍스트에 존재하는 객체에 접근하기에 static 처럼 사용했지만, 해당 객체의 변수의 주도권은 객체이다, 하지만 static 변수는 시스템 단계로 주체 또한 시스템이기에 다른 부분이다...