머리말
회사는 CMS 가비지 컬렉터를 사용하는 시스템을 보유하고 있습니다. JVM의 초기 힙 메모리는 최대 힙 메모리와 같지 않습니다. 그러나 모니터링 정보를 통해 FullGC 이후 서버의 물리적 메모리의 남은 공간이 있음을 알 수 있습니다. FullGC 이후 제가 이해 한 바에 따르면 JVM 프로세스에서 해제 한 메모리의 일부가 실제 메모리로 반환됩니다. 몇 가지 실험을 통해 CMS와 G1의 실제 메모리 반환 메커니즘을 비교하고 검증 해 보겠습니다.
테스트 코드
public class MemoryRecycleTest {
static volatile List<OOMobject> list = new ArrayList<>();
public static void main(String[] args) {
//指定要生产的对象大小为512M
int count = 512;
//新建一条线程,负责生产对象
new Thread(() -> {
try {
for (int i = 1; i <= 10; i++) {
System.out.println(String.format("第%s次生产%s大小的对象", i, count));
addObject(list, count);
//休眠40秒
Thread.sleep(i * 10000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
//新建一条线程,负责清理List,回收JVM内存
new Thread(() -> {
for (; ; ) {
//当List内存到达512M,就通知GC回收堆
if (list.size() >= count) {
System.out.println("清理list.... 回收jvm内存....");
list.clear();
//通知GC回收
System.gc();
//打印堆内存信息
printJvmMemoryInfo();
}
}
}).start();
//阻止程序退出
try {
Thread.currentThread().join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void addObject(List<OOMobject> list, int count) {
for (int i = 0; i < count; i++) {
OOMobject ooMobject = new OOMobject();
//向List添加一个1M的对象
list.add(ooMobject);
try {
//休眠100毫秒
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static class OOMobject {
//生成1M的对象
private byte[] bytes = new byte[1024 * 1024];
}
public static void printJvmMemoryInfo() {
//虚拟机级内存情况查询
long vmFree = 0;
long vmUse = 0;
long vmTotal = 0;
long vmMax = 0;
int byteToMb = 1024 * 1024;
Runtime rt = Runtime.getRuntime();
vmTotal = rt.totalMemory() / byteToMb;
vmFree = rt.freeMemory() / byteToMb;
vmMax = rt.maxMemory() / byteToMb;
vmUse = vmTotal - vmFree;
System.out.println("");
System.out.println("JVM内存已用的空间为:" + vmUse + " MB");
System.out.println("JVM内存的空闲空间为:" + vmFree + " MB");
System.out.println("JVM总内存空间为:" + vmTotal + " MB");
System.out.println("JVM总内存最大堆空间为:" + vmMax + " MB");
System.out.println("");
}
}
JDK8 CMS
JVM 매개 변수 :
-Xms128M -Xmx2048M -XX:+UseConcMarkSweepGC
콘솔이 인쇄하는 내용 :
第1次生产512大小的对象
清理list.... 回收jvm内存....
JVM内存已用的空间为:6 MB
JVM内存的空闲空间为:1202 MB
JVM总内存空间为:1208 MB
JVM总内存最大堆空间为:1979 MB
第2次生产512大小的对象
清理list.... 回收jvm内存....
JVM内存已用的空间为:3 MB
JVM内存的空闲空间为:1097 MB
JVM总内存空间为:1100 MB
JVM总内存最大堆空间为:1979 MB
第3次生产512大小的对象
清理list.... 回收jvm内存....
JVM内存已用的空间为:3 MB
JVM内存的空闲空间为:706 MB
JVM总内存空间为:709 MB
JVM总内存最大堆空间为:1979 MB
第4次生产512大小的对象
清理list.... 回收jvm内存....
JVM内存已用的空间为:3 MB
JVM内存的空闲空间为:120 MB
JVM总内存空间为:123 MB
JVM总内存最大堆空间为:1979 MB
VisualVM에서 모니터링하는 힙 메모리 :
그림에서 힙 메모리 상황에서 JDK8 + CMS 구성에서 JVM이 즉시 운영 체제로 메모리를 반환하지 않고 FullGC 수가 증가함에 따라 점차적으로 반환하고 결국에는 반환되는 것을 알 수 있습니다. 모든
JDK8 G1
JVM 매개 변수 :
-Xms128M -Xmx2048M -XX:+UseG1GC
VisualVM에서 모니터링하는 힙 메모리 :
JDK8 + G1 구성에서 JVM은 각 FullGC 이후에 모든 물리적 메모리를 반환합니다.
JDK11 CMS
JVM 매개 변수 :
-Xms128M -Xmx2048M -XX:+UseConcMarkSweepGC
VisualVM에서 모니터링하는 힙 메모리 :
JDK11 + CMS 구성에서 상황은 JDK8 + CMS와 동일합니다 (JVM은 메모리를 운영 체제로 즉시 반환하지 않지만 FullGC 수가 증가함에 따라 점차적으로 반환하고 결국에는 모두 반환합니다)
JDK11은 JVM 매개 변수를 제공합니다 ShrinkHeapInSteps
. 이 매개 변수를 사용하면 GC 이후에 점진적으로 메모리를 운영 체제로 되돌릴 수 있습니다. JDK11에서이 매개 변수는 기본적으로 활성화됩니다. 이 매개 변수를 끄면 힙 메모리가 어떻게 변경되는지 확인할 수 있습니다.
-Xms128M -Xmx2048M -XX:+UseConcMarkSweepGC -XX:-ShrinkHeapInSteps
VisualVM에서 모니터링하는 힙 메모리 :
ShrinkHeapInSteps
JDK11 + CMS의 구성에서 매개 변수를 끈 후 JVM은 각 FullGC 이후 모든 물리적 메모리를 반환합니다.
JDK11 G1
JDK11은 기본적으로 G1 가비지 수집기를 사용하므로 초기 힙 메모리와 최대 힙 메모리 만 여기에 설정됩니다.
JVM 매개 변수 :
-Xms128M -Xmx2048M
VisualVM에서 모니터링하는 힙 메모리 :
1) JDK11 ShrinkHeapInSteps
은 기본적으로 기본적으로 활성화되어 있지만 여기서는 힙 메모리 변경 사항이 점진적으로 줄어들지 않습니다. 따라서 G1 수집기에서는 ShrinkHeapInSteps
유효하지 않습니다. ShrinkHeapInSteps
매개 변수를 수동으로 끄면 힙 메모리 변경이 위와 비슷하다는 것을 알 수 있습니다.
2) JDK11의 G1과 JDK8의 G1은 메모리에 대한 응답이 다릅니다. 힙 메모리 변경의 관점 에서 JDK11 아래의 G1은 메모리를 최대한 많이 사용하고 재순환하지 않는 경향이 있습니다 . JDK8의 G1은 가능한 한 메모리를 회수하는 경향이 있습니다. 그림에서 JDK8에서 G1의 실제 힙 메모리 크기는 기본적으로 JDK11에서 G1의 절반입니다.
요약
코드는 동일하지만 JVM 매개 변수의 Xms 및 Xmx 설정이 같으면 FullGC가 있는지 여부에 관계없이 힙 메모리 크기가 변경되지 않고 메모리가 운영 체제로 해제되지 않습니다.
GC 후 메모리를 운영 체제로 되 돌리는 방법 :
- 반환 가능 여부는 Xms와 Xmx가 같은지 여부에 따라 다릅니다.
- 반환시기는 주로 JDK 버전과 가비지 수집기 유형에 따라 다릅니다.
FullGC에서만 힙 메모리가 축소되고 OS로 복귀가 트리거 될 수 있습니다. YGC는 JVM이 운영 체제에 메모리를 적극적으로 반환하도록 할 수 없습니다.
힙 메모리 조정으로 인한 성능 손실을 줄이고 힙 메모리 조정으로 인한 메모리 없음 위험을 줄이기 위해 Xms 및 Xmx를 일관되게 유지하십시오.
참고:
https://segmentfault.com/a/1190000019856974
https://www.cnblogs.com/androidsuperman/p/11743103.html
http://blog.dutycode.com/archives/jvmjvm%E7%9A%84xms%E5%8F%82%E6%95%B0%E5%92%8Clinuxtop%E5%91%BD%E4%BB%A4% E7 % 9A % 84res % E5 % 85 % B3 % E7 % B3 % BB