>  기사  >  Java  >  Java 메모리 할당 및 재활용 전략에 대한 간략한 토론

Java 메모리 할당 및 재활용 전략에 대한 간략한 토론

巴扎黑
巴扎黑원래의
2017-06-26 10:41:411069검색

1. 소개

Java 기술 시스템에서 언급되는 자동 메모리 관리는 결국 메모리 할당과 재활용이라는 두 가지 문제입니다. 오늘은 Java 재활용에 대한 관련 지식을 말씀드렸습니다. Java 객체. 메모리 할당. 평신도의 관점에서 개체의 메모리 할당은 힙에 대한 할당입니다. 개체는 주로 신세대 Eden에 할당됩니다(메모리의 개체 생성은 가비지 수집 중에 보충됩니다. 자세한 내용을 알고 싶다면 다음을 참조하세요. "자바 가상 머신 심층 이해" 참조) 로컬 스레드 할당 버퍼가 시작되면 스레드 우선순위에 따라 TLAB에 할당됩니다. 어떤 경우에는 Old Generation에 직접 할당되기도 합니다.

2. 클래식 할당 전략

1. 객체는 Eden에 먼저 할당됩니다

일반적으로 Eden에 할당할 공간이 충분하지 않으면 jvm은 Minor GC를 시작합니다. 할당된 공간이 여전히 충분하지 않은 경우 아래에 언급된 다른 조치가 있습니다.

가상 머신의 홀수 로그 매개변수 -XX:+PrintGCDetails를 설정합니다. 가비지 수집 중에 메모리 재활용 로그가 인쇄되고, 프로세스가 종료되면 각 메모리 영역의 현재 할당이 출력됩니다. 구체적인 예를 살펴보겠습니다. 먼저 jvm 매개변수 -Xms20m -Xmx20m -Xmn10m을 설정해야 합니다. 이 세 매개변수는 Java 힙 크기가 20M이고 10M이 새 세대에 할당될 수 없으며 나머지 10M임을 나타냅니다. 구세대에 할당됩니다. -XX:SurvivorRatio=8은 새로운 세대의 jvm에서 Eden과 Survivor의 기본 비율입니다. 기본값은 8:1입니다. 그 이유는 다음 세대의 객체 중 98%가 다음 GC에서 재활용될 것이기 때문에 가비지 컬렉션을 위한 복사 알고리즘을 사용하는 것이 매우 적합하기 때문입니다. 따라서 새로운 세대의 10M 메모리 중 8M은 Eden입니다. 1M은 Survivor이고 나머지 1M은 사용되지 않습니다. 복사 알고리즘에 협력하는 메모리 블록도 Survivor입니다.

 1 public class ReflectTest { 2  3     private static final int _1MB = 1024*1024; 4      5     public static void testAllocation(){ 6         byte[] allocation1 , allocation2 , allocation3 , allocation4; 7         allocation1 = new byte[2 * _1MB]; 8         allocation2 = new byte[2 * _1MB]; 9         allocation3 = new byte[2 * _1MB];10         allocation4 = new byte[6 * _1MB];11     }12     13     public static void main(String[] args) {14         ReflectTest.testAllocation();15     }16     17 }

출력은 다음과 같습니다

Heap
 PSYoungGen      total 9216K, used 6651K [0x000000000b520000, 0x000000000bf20000, 0x000000000bf20000)
  eden space 8192K, 81% used [0x000000000b520000,0x000000000bb9ef28,0x000000000bd20000)
  from space 1024K, 0% used [0x000000000be20000,0x000000000be20000,0x000000000bf20000)
  to   space 1024K, 0% used [0x000000000bd20000,0x000000000bd20000,0x000000000be20000)
 PSOldGen        total 10240K, used 6144K [0x000000000ab20000, 0x000000000b520000, 0x000000000b520000)
  object space 10240K, 60% used [0x000000000ab20000,0x000000000b120018,0x000000000b520000)
 PSPermGen       total 21248K, used 2973K [0x0000000005720000, 0x0000000006be0000, 0x000000000ab20000)
  object space 21248K, 13% used [0x0000000005720000,0x0000000005a07498,0x0000000006be0000)

eden이 81%를 점유하고 있는 것을 볼 수 있습니다. 이는 할당1, 할당2, 할당3이 모두 차세대 Eden에 할당되었음을 나타냅니다.

2. Large 객체는 Old Generation에서 직접 할당됩니다.

Large 객체는 긴 문자열 및 배열과 마찬가지로 저장하기 위해 많은 양의 연속 메모리 공간이 필요한 객체를 말합니다. 큰 개체는 가상 머신의 메모리 분배에 좋지 않습니다. 한 라운드 동안만 살아남는 큰 개체가 많으면 JVM이 처리하기가 더 어려워지며 코드를 작성할 때 이러한 문제를 피해야 합니다. -XX:PretenureSizeThreshold 매개변수는 가상 머신에서 제공되며, 이 값보다 큰 개체는 이전 세대에 직접 할당됩니다. 이는 Eden 영역과 Survivor 영역 사이에 대량의 메모리 복사를 방지하는 것입니다. 앞에서 언급한 재활용 알고리즘과 복사 알고리즘에 대해서는 앞에서 언급한 바 있으므로 자세히 설명하지 않겠습니다.

public class ReflectTestBig {private static final int _1MB = 1024*1024;    public static void testAllocation(){byte[] allocation2 , allocation3 , allocation4;allocation2 = new byte[2 * _1MB];
        allocation3 = new byte[2 * _1MB];
        allocation4 = new byte[6 * _1MB];
    }    public static void main(String[] args) {
        ReflectTestBig.testAllocation();
    }
    
}

출력은 다음과 같습니다

Heap
 PSYoungGen      total 8960K, used 4597K [0x000000000b510000, 0x000000000bf10000, 0x000000000bf10000)
  eden space 7680K, 59% used [0x000000000b510000,0x000000000b98d458,0x000000000bc90000)
  from space 1280K, 0% used [0x000000000bdd0000,0x000000000bdd0000,0x000000000bf10000)
  to   space 1280K, 0% used [0x000000000bc90000,0x000000000bc90000,0x000000000bdd0000)
 PSOldGen        total 10240K, used 6144K [0x000000000ab10000, 0x000000000b510000, 0x000000000b510000)
  object space 10240K, 60% used [0x000000000ab10000,0x000000000b110018,0x000000000b510000)
 PSPermGen       total 21248K, used 2973K [0x0000000005710000, 0x0000000006bd0000, 0x000000000ab10000)
  object space 21248K, 13% used [0x0000000005710000,0x00000000059f7460,0x0000000006bd0000)

할당4가 -XX:PretenureSizeThreshold=3145728 설정을 초과한 것을 볼 수 있으며, 할당4는 Old Generation에 직접 할당되어 Old Generation 점유율은 60%입니다. . -XX:PretenureSizeThreshold=3145728 설정은 -XX:PretenureSizeThreshold=3m으로 쓸 수 없습니다. 그렇지 않으면 jvm이 이를 인식하지 못합니다.

3. 장기적으로 살아남은 개체는 Old Generation으로 들어갑니다

가상 머신은 메모리 관리를 위해 Banded Collection 개념을 채택하므로 메모리 재활용은 어떤 개체가 새 세대에 배치되어야 하는지 식별해야 합니다. Old Generation에 배치됩니다. 이 목적을 달성하기 위해 jvm은 각 객체에 대한 연령 카운터(Age)를 정의합니다. 개체가 Eden에서 태어나 첫 번째 Minor GC에서 살아남아 Survivor에 저장할 수 있는 경우 Survivor로 이동되고 개체의 age는 1로 설정됩니다. 개체가 Minor GC를 벗어날 때마다 해당 연령은 1씩 증가합니다. 해당 연령이 1년 임계값을 초과하면 개체는 Old Generation으로 승격됩니다. 이 임계값 jvm의 기본값은 15이며 -XX:MaxTenuringThreshold로 설정할 수 있습니다.

   m = 1024 * 1024  [] a1 =  [1 * m / 4[] a2 =  [7 *[] a3 =  [3 * m];

출력은 다음과 같습니다

[GC [DefNew: 7767K->403K(9216K), 0.0062209 secs] 7767K->7571K(19456K), 0.0062482 secs]   
[Times: user=0.00 sys=0.00, real=0.01 secs]   
a3 ok  
Heap  
 def new generation   total 9216K, used 3639K [0x331d0000, 0x33bd0000, 0x33bd0000)  
  eden space 8192K,  39% used [0x331d0000, 0x334f9040, 0x339d0000)  
  from space 1024K,  39% used [0x33ad0000, 0x33b34de8, 0x33bd0000)  
  to   space 1024K,   0% used [0x339d0000, 0x339d0000, 0x33ad0000)  
 tenured generation   total 10240K, used 7168K [0x33bd0000, 0x345d0000, 0x345d0000)  
   the space 10240K,  70% used [0x33bd0000, 0x342d0010, 0x342d0200, 0x345d0000)  
 compacting perm gen  total 12288K, used 381K [0x345d0000, 0x351d0000, 0x385d0000)  
   the space 12288K,   3% used [0x345d0000, 0x3462f548, 0x3462f600, 0x351d0000)  
    ro space 10240K,  55% used [0x385d0000, 0x38b51140, 0x38b51200, 0x38fd0000)  
    rw space 12288K,  55% used [0x38fd0000, 0x396744c8, 0x39674600, 0x39bd0000)

a2가 한 번 살아남은 것을 볼 수 있으며 age는 1입니다. 이는 -XX:MaxTenuringThreshold=1 집합을 만족하므로 a2는 Old Generation에 진입하고 a3는 New Generation에 진입했습니다.

4. 동적 개체 수명 결정

다양한 프로그램의 메모리 상태에 더 잘 적응하기 위해 가상 머신은 개체의 수명이 항상 -XX:MaxTenuringThreshold에 의해 설정된 값에 도달해야 할 것을 요구하지는 않습니다. Old Generation으로 승격되며, Survivor 공간에 있는 동일한 Age의 모든 객체의 크기의 합이 Survivor 공간의 절반보다 크면 해당 Age보다 크거나 같은 객체는 Old Generation 영역으로 직접 진입할 수 있습니다. -XX:MaxTenuringThreshold에 설정된 값에 도달합니다.

5. 공간 할당 보장

Minor GC가 발생하면 가상 머신은 Old 세대에 대한 각 승격의 평균 크기가 Old 세대의 남은 공간보다 큰지 여부를 감지하여 FUll GC를 수행합니다. 직접 수행됩니다. 미만인 경우에는 HandlerPromotionFailyre 설정이 보장 실패를 허용하는지 확인하고, 허용하는 경우 Minor GC만 수행하고, 허용하지 않는 경우에는 FUll GC도 개선합니다. 즉, 신세대 Eden이 수정된 개체를 저장할 수 없는 경우 해당 개체는 Old 세대에 저장됩니다.

3. 일반적으로 사용되는 jvm 매개변수 설정

1. -Xms: 초기 힙 크기, 기본값(MinHeapFreeRatio 매개변수 조정 가능) 여유 힙 메모리가 40% 미만이면 JVM은 최대 한도까지 힙을 늘립니다. -Xmx.

2.

3. -Xmn: Young Generation 크기(1.4or lator), 여기의 크기는 jmap -heap에 표시된 New gen과 다릅니다.
 전체 힙 크기 = Young Generation 크기 + Old Generation 크기 + 영구 생성 크기.
  Young Generation을 늘리면 Old Generation의 크기가 줄어듭니다. 이 값은 시스템 성능에 더 큰 영향을 미칩니다. Sun은 공식적으로 전체 힙의 3/8 구성을 권장합니다.

4. -XX:NewSize: Young Generation 크기를 설정합니다(1.3/1.4의 경우).

5. -XX:MaxNewSize: 젊은 세대의 최대값(1.3/1.4의 경우).

6. -XX:PermSize: 영구 생성(perm gen)의 초기 값을 설정합니다.

7. -XX:MaxPermSize: 영구 생성의 최대 크기를 설정합니다.

8, -Xss: 각 스레드의 스택 크기. JDK5.0 이후에는 각 스레드의 스택 크기가 1M입니다. 이전에는 응용 프로그램 스레드에 필요한 메모리 크기가 256K였습니다. 동일한 물리적 Under 메모리에서는 이 값을 줄이면 더 많은 스레드가 생성될 수 있지만 운영 체제에서는 여전히 프로세스의 스레드 수에 제한이 있으며 경험 값은 약 3000~입니다. 5000.

9, -XX:NewRatio: Young Generation(Eden 및 Survivor 영역 2개 포함)과 Old Generation(Persist Generation 제외)의 비율, -XX:NewRatio=4는 Young Generation과 Survivor 영역의 비율을 의미합니다. Old Generation은 1:4이고 Young Generation은 전체 스택의 1/5을 차지합니다. Xms=Xmx 및 Xmn이 설정된 경우 이 매개변수를 설정할 필요가 없습니다.

10, -XX:SurvivorRatio: Eden 영역과 Survivor 영역의 크기 비율을 8로 설정한 후 두 개의 Survivor 영역과 하나의 Eden 영역의 비율이 2:8이고 하나의 Survivor 영역이 1/10을 차지합니다. 전체 젊은 세대의.

11. -XX:LargePageSizeInBytes: 메모리 페이지의 크기는 너무 크게 설정할 수 없으며 이는 Perm의 크기에 영향을 미칩니다.

12, -XX:+DisableExplicitGC: System.gc() 끄기

13, -XX:MaxTenuringThreshold: 가비지의 최대 수명을 0으로 설정하면 Young Generation 개체가 Survivor 영역을 통과하지 않고 직접 이동합니다. Old Generation의 수가 많은 애플리케이션의 경우 효율성을 높일 수 있으며, 이 값을 더 크게 설정하면 Survivor 영역에 Young Generation 객체가 여러 번 복사되므로 생존 시간이 늘어날 수 있습니다. Young Generation의 객체 수를 늘리고 Young Generation이 재활용될 확률은 직렬 GC에서만 유효합니다.

14, -XX:PretenureSizeThreshold: 개체가 크기를 초과하면 개체가 이전 세대에서 직접 할당됩니다. 새 세대가 병렬 스캐빈지 GC를 사용하는 경우 단위 바이트가 유효하지 않습니다. 이전 세대에서 직접 할당되는 또 다른 상황은 다음과 같습니다. 큰 배열 개체 및 배열에는 외부 참조 개체가 없습니다.

15, -XX:TLABWasteTargetPercent: 에덴 지역의 TLAB 비율입니다.

4. 보충

Minor GC와 FUll GC의 차이점:

 신세대 GC(Minor GC): 대부분의 Java 객체가 GC의 첫 번째 라운드를 벗어날 수 없기 때문에 새로운 세대에서 발생하는 가비지 수집 작업을 말합니다. , 그래서 Minor GC가 매우 자주 사용되며 일반적으로 복구 속도가 상대적으로 빠릅니다.

  Old Generation GC(FULL GC/Major GC): Old Generation에서 발생하는 GC를 말합니다. Major GC가 나타나면 적어도 하나의 Minor GC를 동반하는 경우가 많습니다(절대적인 것은 아니지만, 직접적으로 포함되는 경우도 있음). ParallelScavenge 수집기의 수집 전략) Major GC 선택 프로세스를 수행합니다. Major GC의 속도는 일반적으로 Minor GC보다 10배 이상 느립니다.

위 내용은 Java 메모리 할당 및 재활용 전략에 대한 간략한 토론의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.