It is where I record the error fixes or learning during the development process.
Stack
Heap
주의) Heap 영역에 있는 데이터는 함수 내부에서 파라미터로 받아서 변경하고
함수 호출이 종료된 시점에 변경 내역이 반영되는 것을 확인 !!
List<String> list = new ArrayList<>();
solve(list);
==> solve 함수에서 list 를 변경하면 그대로 반영됨! 힙영역에 있으므로 !
주의 ) 아래와 같이 Integer는 heap 영역에서 관리하는 Object 타입인데 변경되지 않는 이유는 String과 동일하게 불변객체(immutable)이기 때문
Integer a = 10;
solve(a);
=> solve 함수에서 값을 변경하면 바뀌지 않는다!!!
=> 실행 순서는 a가 가르키는 10은 heap 에 생성되지만 solve 라는 함수로
param = 10 으로 넘어갈때 Wrapper class 는 기존에 레러펀스하고 있던 값을
새롭게 생성된 오브젝트를 만들기 때문에 함수가 끝나고 나면 변경된 값 pop 되기때문 !!!
Java Compiler
Class Loader
Execution Engine
Class Loader를 통해 JVM 내부로 넘어와 Runtime Data Area(JVM 메모리)에 배치된 Byte code들을 기계어로 변경해 명령어 단위로 실행하는 역할
1) 인터프리터 컴파일 방식 : 명령어를 하나하나 실행 2) JIT(Just-In-Time) 컴파일 방식 :
GC
String url = "abc";
url += "def";
=> + 하는 순간 새롭게 "abcdef" 라는 객체를 만들고 url이 레퍼런스
==> 처음 "abc" 오브젝트는 Unreachable 오브젝트라 하고 GC의 대상!!
Minor GC와 Major GC
JVM의 Heap 영역은 Young, Old, Perm 세 영역으로 나뉨
young 영역에서 발생한 GC를 Minor GC, 나머지 두 영역에서 발생한 GC를 Major GC(Full GC)
young 영역 : 새롭게 생성한 객체가 위치, 대부분의 객체가 금방 unreachable 상태가 되기 때문에 많은 객체가 Young 영역에 생성되었다가 사라짐
old 영역 : Young영역에서 reachable 상태를 유지해 살아남은 객체가 여기로 복사됨. 대부분 Young 영역보다 크게 할당하며, 크기가 큰 만큼 Young 영역보다 GC는 적게 발생
Perm 영역 : Method Area라고도 함 / 클래스와 메소드 정보와 같이 자바 언어 레벨에서는 거의 사용되지 않음
Reachability
Java의 GC는 가비지 객체를 판별하기 위해 reachability 라는 개념을 사용함
어떤 객체에 유효한 참조가 있으면 reachable 없으면 unreachable로 구분하고 가비지로 간주함
바꿔 말하면 객체에 대한 reachability를 제어 할수 있다면 코드를 통해 GC에 일부 관여하는 것이 가능
java는 이를 위해서 SoftReference, WeakReference 등 제공
캐시 등을 만들때 메모리 누수 조심 ( 캐시의 키가 원래 데이터에서 삭제된다면 캐시 내부의 키와 값은 더이상 의미 없는 데이터이지만 GC는 삭제된 캐시의 키를 가비지로 인식 못함 ! ) => 캐시에 Weak Reference를 넣어준다면 이러한 문제 방지 가능 (WeakHashMap)
Runtime Data Areas(JVM 메모리)
Method, Runtime Constant Pool, Heap, Stack, PC Register, Native Method Stack)
1) Method area 영역 : 클래스 멤버 변수
JVM 옵션 정리
-verbose:gc //
-XX:+PrintCommandLineFlag // 어떤 가비지 컬렉터를 사용하는지 볼수 있음
-Xms
-Xmx
-Xmx16m -verbose:gc -XX:+PrintCommandLineFlags // OutofMemoryError를 빨리 내기 위해 jvm 옵션 설정
1) 프로그램이 메모리 부족으로 죽는 경우
public class ListGCTest {
public static void main(String[] args) throws Exception {
List<Integer> li = IntStream.range(1, 100).boxed().collect(Collectors.toList());
for (int i=1; true; i++) {
if (i % 100 == 0) {
Thread.sleep(100);
}
IntStream.range(0, 100).forEach(li::add);
}
}
}
2) 가비지 컬렉터가 열일하여 프로그램이 죽지 않는 경우
public class ListGCTest {
public static void main(String[] args)throws Exception {
List<Integer> li = IntStream.range(1, 100).boxed().collect(Collectors.toList());
for (int i=1; true; i++) {
if (i % 100 == 0) {
li = new ArrayList<>();
Thread.sleep(100);
}
IntStream.range(0, 100).forEach(li::add);
}
}
}
1) Metaspace
1-1) PermGen 은 자바 7까지 메타데이터를 저장하던 영역이였고 Heap의 일부
==> 자바 8부터 클래스들은 모두 힙이 아닌 네이티브 메모리를 사용하는 Metaspace에 할당 됨
public class MetaspaceTest {
static javassist.ClassPool cp = javassist.ClassPool.getDefault();
public static void main(String[] args) throws Exception{
for (int i = 0; ; i++) {
if (i % 1000 == 0) Thread.sleep(100);
Class c = cp.makeClass("test" + i).toClass();
}
}
}
2) Heap - Old & Young ( Eden, Survivor)
가비지 컬렉션 프로세스
새로운 오브젝트는 Eden 영역에 할당 됨. 두개의 Survivor Space 는 비워진 상태로 시작함
Eden 영역이 가득차면, MinorGC가 발생함
MinorGC가 발생하면, Reachable 오브젝트들은 S0 으로 옮겨진다. Unreachable 오브젝트들은 Eden 영역이 클리어 될때 함께 메모리에서 사라짐 !!
다음 Minor GC가 발생할때, Eden 역에는 3번과 같은 과정이 발생함. Unreachable 오브젝트들은 지워지고, Reachable 오브젝트들은 Survivor Space 로 이동함. 기존에 S0 에 있었던 Reachable 오브젝트들은 S1으로 옮겨지는데, 이째 age값이 증가되어 옮겨진다. 살아남은 모든 오브젝트들이 S1으로 모두 옮겨지면, S0 와 Eden은 클리어 됨. => Survivor Space 에서 Survivor Space 로의 이동은 이동할때마다 age 값이 증가한다.
다음 MinorGC가 발생하면, 4번과정이 반복되는데 S1 이 가측차 있었으므로 S1에서 살아남은 오브젝트들은 S0로 옮겨지면서 Eden 과 S1 은 클리어 된다. 이때에도 age값이 증가되어 옮겨진다.
Young Generation 에서 계속해서 살아남으며 age 값이 증가하는 오브젝트들은 age 값이 특정값 이상이 되면 Old Generation 으로 옮겨지는데 이 단계를 Promotion 이라고 함 !!!
MinorGC가 계속 반복되면, Promtion 작업도 꾸준히 발생하게 됨!!
Promiton 작업이 계속해서 반복되면서 Old Generation 이 가득차게 되면 MajorGC가 발생하게 됨!!!
용어 정리
VisualVM
jdk 1.6 이상부터는 별도의 설치 없이 실행 가능 ( /bin/jvisualvm )
jconsole을 대체하는 툴 => JVM을 실시간으로 모니터링 할수 있는 오픈소스 기반 툴
heap 덤프 및 쓰레드 덤프 가능
VisualGC 플러그인 설치 (VisualVM 에서 Toll > Plugins > Available Plugins 에서 install )
1) WeakReference vs StrongReference
StrongReference : new() 객체 생성 방법
=> 가장 먼저 참조되어지는 root객체로부터 사슬이 계속 연결되어 있는가 여부에 따라서 GC의 대상
=> GC에서 무조건 제외되기 때문에 메모리 누수 유발 가능
WeakReference : 명확하게 GC에 의한 메모리상의 회수 대상이 됨 => 짧은 시간, 자주 쓰일 수 있는 객체를 이용할때 유용
2) WeakReference 구현
WeakReference wr;
public String getFileContent(String filename) {
// WeakReference 에 의해 파일 내용이 보존되어 있는지 체크
String fileContent = (wr != null) ? wr.get() : null;
if(fileContent == null) {
// 글 내용이 비었으면 파일 이름으로 내용을 읽어와 채워준다.
fileContent = fileToString(filename);
// 채워진 글 내용을 WeakReference 에 저장한다.
wr = new WeakReference(fileContent);
}
return fileContent;
}
GC가 발생하기 전까진 WeakReference가 글 내용을 가지고 있으면 캐쉬 역할을 하지만 GC 발생하면 wr.get()은 null을 반환하고 다시채움으로써 메모리 누수 방지 !
생성한 객체는 get 메소드로 얻어옴
WeakHashMap
참고 : https://yaboong.github.io/java/2018/06/09/java-garbage-collection/