프로그래머호이잇

JAVA Heap Memory, Garbage Collector 본문

java

JAVA Heap Memory, Garbage Collector

호이잇! 2022. 1. 3. 23:32

JVM 메모리 구조

-Xms : 초기 JVM이 생성될때 설정한 Heap 메모리 값
-Xmx : JVM이 최대 사용 가능한 Heap 메모리 값

e.g. -Xms1200m –Xmx1200m

JAVA Heap 메모리 구조는 Java8 기준으로 Eden 영역, Survivor 영역, Old 영역이 존재한다.

Eden, Survivor 영역을 Young Generation, old 영역을 Old Genertaion 으로 관리된다.

Young Generation 과 Old Genertaion 영역에서 참조되지 않는 변수들은 모두 Garbage Collector(이하 GC) 대상이 된다.

GC 는 Minor GC, Major GC 2가지 로 나뉜다.

Garbage Collector 과정

1. Garbage Collector 가 Stack의 모든 변수를 스캔하면서 참조하는 객체를 참조하고 있는지 마킹한다.

2. Reachable Object 가 참조하고 있는 객체도 찾아서 마킹한다. (객체가 참조하고 있는 객체를 찾는 과정)

3. 마킹되지 않는 객체를 Heap에서 제거한다.

크게 1,2 Mark 와 3 Swap 로 나뉜다. 

Minor GC

Young Generation 영역에서 일어나는 GC 를 말한다.

순서

1. Eden 영역이 모두 채워졌을 시 Minor GC 가 발생한다.

2. 참조되고 있는 객체를 Survivor0 영역으로 이동 시킨다.

1,2 를 반복하여 Survivor0 영역이 모두 채워질 시 Minor GC 가 발생한다

3. Survivor0 영역에서 참조되고 있는 객체의 Age를 +1 후 Survivor1 영역으로 이동 시킨다.

4. 이후 Eden 영역에서 발생하는 Minor GC 는 Survivor1 영역으로 이동한다.

3,4 를 반복하여 Survivor1 영역이 모두 채워질 시 Survivor 영역으로 이동시키는 Minor GC 를 발생시킨다.

이러한 순서로 반복되며 특정 Age가 넘어가는 객체는 Old Generation 영역으로 이동시킨다.

Major GC

Old Generation 영역에서 일어나는 GC를 말한다.

Old 영역에 있는 모든 객체들을 검사하며 참조되고 있는지 확인한다.
참조되지 않은 객체들을 모아 한 번에 제거한다.
Minor GC보다 시간이 훨씬 많이 걸리고 실행중에 GC를 제외한 모든 쓰레드가 중지한다.

 

 

 

JAVA 8 Heap 메모리 구조

JVM 8 Heap 메모리 구조

Java 8 이전에는 Metaspace 영역이 아닌 Permanent 영역이 존재하였다. Permanent 영역은 Class의 Meta 정보나 Method의 Meta 정보, Static 변수와 상수 정보들이 저장되는 공간으로 활용되었다. 하지만 Java 8 버전부터는 기존의 Permanent 영역이 Native 영역으로 이동하여 Metaspace 영역으로 변경되었다.

 

Eden Space [Young Generation]
처음 생성된 모든 객체는 에덴 영역에 존재합니다. JVM에 의해 정해진 임계치에 도달하면 minor gc가 수행됩니다. minor gc가 수행된다.
새롭게 생성된 객체 대부분이 이 영역으로 위치한다.
이 곳이 가득차면 minor gc가 발생한다.
minor gc가 발생하면 살아있는 객체들만 체크하고 나머지는 다 없애버린다.
살아남은 객체들 중 더 오래 쓸 것 같은 것들은 Tenured 영역으로 옮긴다.


Survivor 1 (From)
에덴 영역으로부터 살아남은 객체가 담깁니다. 이전의 gc 과정에서는 'To'의 역할을 하던 영역입니다.


Survivor 2 (To)
gc가 수행될 때, 에덴 영역과 'From'의 역할을 하던 Survivor 영역에서 살아남은 객체들이 담깁니다.


Tenured [Old Generation]
Survivor 영역의 객체가 minor gc에서 살아남아 다른 Survivor 영역으로 이동할 때마다 객체의 Age가 증가합니다. 이 Age가 일정 이상이 되면 Tenured 영역으로 이동하게 됩니다(Promotion).
Promotion의 기준이 되는 Age는 -XX:MaxTenuringThreshold 옵션으로 설정할 수 있습니다. Java SE 8 에서의 default 값은 15이며, 설정 가능한 범위는 0 ~ 15 입니다.
Young 영역에서 살아남은 객체들은 이 곳으로 복사된다.
이 곳이 가득차면 major gc(혹은 full gc)가 발생한다.
대부분 Young 영역보다 크게 할당하며, 크기가 큰 만큼 Young 영역보다 gc는 적게 발생한다.
major gc는 minor gc 보다 오래걸린다.

 

전체 Heap 메모리 설정

-Xms : 초기 JVM이 생성될때 설정한 Heap 메모리 값
-Xmx : JVM이 최대 사용 가능한 Heap 메모리 값

e.g. -Xms1024m –Xmx2048m

New Old간에 메모리 비율 설정

-XX:NewRatio=x    x 비율

e.g. -XX:NewRatio=2    ---> Yong:Old 비율이 1:2이라는 의미

New 영역 내부의 Eden, Survivor 영역비

-XX:SurvivorRatio 

e.g. -XX:SurvivorRatio=8
--->  Young 전제 2GB 에서 SurvivorRatio가 8이면 Eden영역이 1.6GB
        Survivor영역이 0.4GB이고 Survivor 영역은 S1,S2로 2개의 영역으로
       이루어져 있으므로 각각 0.2GB, 결과적으로 Eden과 Survivor 비율은 1:8

Metaspace 영역 크기

-XX:MetaspaceSize=128m

-XX:MaxMetaspaceSize=256m

 

GC 와 JVM 메모리 구조 관련하여 테스트를 하기위해 아래와 같이 코딩하여 테스트를 해보았습니다.

 

@RestController
public class HomeController {
    Map<String, List<User>> testMap = new HashMap<>();
    int num = 0;
    @GetMapping("/")
    public String index() {
        int testCount = 100000;
        List<User> list = new ArrayList<>();
        for(int i=0; i<testCount; i++){
            User user = new User();
            user.setUserNm(testCount+Thread.currentThread().getName());
            user.setEmpNo(Thread.currentThread().getName()+testCount);
            user.setId(new Random().nextLong());
            list.add(user);
        }
        testMap.put(num+Thread.currentThread().getName(), list);
        String reVal = "현재 메모리 사용량 = " + (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory());
        System.out.println(Thread.currentThread().getName());
        num++;
        return reVal;
    }
}

 

 

모니터링 시 Eden Space

Eden Space

 

 

Survivor Space

 

old Gen

 

열심히 돌리다보면 마무리로 아래와 같은 에러가 발생합니다.

GC overhead limit exceeded
java.lang.OutOfMemoryError: GC overhead limit exceeded
	at java.lang.AbstractStringBuilder.<init>(AbstractStringBuilder.java:68)
	at java.lang.StringBuilder.<init>(StringBuilder.java:89)
	at com.lsw.menagement.controller.HomeController.index(HomeController.java:19)
	at sun.reflect.GeneratedMethodAccessor40.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
	at

 

 

 

 

*위 글은 아래 블로그의 글을 참고하여 개인공부 겸 작성한 글입니다.

참조링크 :  https://sharplee7.tistory.com/m/54

 

Java Heap 메모리 옵션

JVM의 메모리 영역은 Runtime Data Areas내에 그 기능에 따라 5가지 영역으로 나뉘어 있는데 그 중에서도 new 키워드를 통해 객체를 생성하고 모든 개체와 해당 인스턴스 변수 및 배열이 저장되는 영역

sharplee7.tistory.com

 

 

'java' 카테고리의 다른 글

JVM  (0) 2021.12.28
Mybatis #과 $ 차이  (0) 2019.08.28
jvm 메모리 관련 내용  (0) 2019.08.26
Mabatis <where> 의미  (0) 2017.11.10
@param 사용이유  (0) 2017.10.25