JVM原理实践
光懂得一堆原理是不够的,毕竟是从别人口中说出来的,我们一起来试试吧
首先,内存溢出的几种情况及其导致原因
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import sun.misc.Unsafe;
/**
* 堆内存溢出:java.lang.OutOfMemoryError
* 栈内存溢出:java.lang.StackOverflowError
*/
public class JVMOverFlow {
static class OOMObject {
}
/**
* 堆溢出
* -verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=E:\jvm
* -verbose:gc
* -Xms20M 堆空闲占用
* -Xmx20M 堆最大占用
* -Xmn10M 堆最小占用
* -XX:+PrintGCDetails
* -XX:SurvivorRatio=8 存活比2:8
* -XX:+HeapDumpOnOutOfMemoryError dump出当前的内存堆转储快照
* -XX:HeapDumpPath=E:\jvm 指定路径(转储文件)
*/
public static void test1() {
List<OOMObject> list = new ArrayList<OOMObject>();
while(true) {
list.add(new OOMObject());
}
}
/**
* 栈溢出
* -verbose:gc -Xss128k -XX:SurvivorRatio=8 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=E:\jvm
* -verbose:gc
* -Xss128k 栈容量
* -XX:SurvivorRatio=8 存活比1:8
* -XX:+HeapDumpOnOutOfMemoryError dump出当前的内存堆转储快照
* -XX:HeapDumpPath=E:\jvm 指定路径(转储文件)
*/
int stackLength = 1;
public void test2() {
stackLength++;
System.out.println("id:" + Thread.currentThread().getId() + " count:" + stackLength);
this.test2();
}
/**
* 堆栈同时泄漏导致的内存溢出
*/
public void stackLeakByThread() {
while(true) {
Thread t = new Thread(() -> {
while(true) {
}
});
t.start();
}
}
/**
* [Metaspace: 2585K->2585K(1056768K)],
* 堆溢出
* -XX:PermSize=10M 方法区默认大小(已失效)
* -XX:MaxPermSize=10M 方法区最大大小(已失效)
*/
public void test3() {
List<String> list = new ArrayList<String>();
int i = 0;
while(true) {
list.add(String.valueOf(i++).intern());
}
}
/**
* -XX:MaxDirectMemorySize=10M
*/
public void test4() throws IllegalArgumentException, IllegalAccessException {
Field unsafeField = Unsafe.class.getDeclaredFields()[0];
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
while(true) {
unsafe.allocateMemory(1000);
}
}
/**
* 引用计数算法的缺陷
*/
public Object instance = null;
public void test5() {
JVMOverFlow objA = new JVMOverFlow();
JVMOverFlow objB = new JVMOverFlow();
objA.instance = objB;
objB.instance = objA;
objA = null;
objB = null;
}
public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
// test1();
new JVMOverFlow().test2();
// new Thread(() -> {new JVMOverFlow().test2();}).start();
// new JVMOverFlow().stackLeakByThread();
// new JVMOverFlow().test3();
// new JVMOverFlow().test4();
// new JVMOverFlow().test5();
}
// [Full GC (Ergonomics) [PSYoungGen: 8192K->4607K(9216K)] [ParOldGen: 6700K->9194K(10240K)] 14892K->13802K(19456K), [Metaspace: 3015K->3015K(1056768K)], 0.0865929 secs] [Times: user=0.17 sys=0.00, real=0.09 secs]
// [GC (Allocation Failure) [PSYoungGen: 8192K->1021K(9216K)] 8192K->1305K(19456K), 0.0017204 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
}
GC策略
/**
* GC 策略
*/
public class GCPolicy {
private static final int _1MB = 1024 * 1024;
/**
* 对象优先在Eden区分配
* VM参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC
*/
@SuppressWarnings("unused")
public static void testAllocation() {
byte[] allocation1, allocation2, allocation3, allocation4;
allocation1 = new byte[2 * _1MB];
allocation2 = new byte[2 * _1MB];
allocation3 = new byte[2 * _1MB]; // Eden:7130K Survivor:0K Tenure:0K
allocation4 = new byte[4 * _1MB]; // Eden:4728K Survivor:0K Tenure:6144K Minor GC后
}
/**
* 大对象直接进入老年代
* VM参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC -XX:PretenureSizeThreshold=3145728
*
*/
@SuppressWarnings("unused")
public static void testPretenureSizeThreshold() {
byte[] allocation;
allocation = new byte[4 * _1MB]; //直接分配在老年代中
}
/**
* 长期存活的对象将进入老年代
* VM参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=1 -XX:+UseSerialGC
* -XX:+PrintTenuringDistribution
*/
@SuppressWarnings("unused")
public static void testTenuringThreshold() {
byte[] allocation1, allocation2, allocation3, allocation4, allocation5;
allocation1 = new byte[_1MB / 4]; // 什么时候进入老年代决定于XX:MaxTenuringThreshold设置
allocation2 = new byte[4 * _1MB];
allocation3 = new byte[4 * _1MB];
allocation4 = new byte[4 * _1MB];
// allocation5 = new byte[4 * _1MB];
}
/**
* 动态对象年龄判定(survivor区)
* VM参数:-verbose:gc -Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=15 -XX:+UseSerialGC
* -XX:+PrintTenuringDistribution
*/
@SuppressWarnings("unused")
public static void testTenuringThreshold2() {
byte[] allocation1, allocation2, allocation3, allocation4;
allocation1 = new byte[_1MB / 4]; // allocation1+allocation2大于survivo空间一半
allocation2 = new byte[_1MB / 4];
allocation3 = new byte[4 * _1MB];
allocation4 = new byte[4 * _1MB];
allocation4 = null;
allocation4 = new byte[4 * _1MB];
}
/**
* 空间分配担保策略
* VM参数:-Xms20M -Xmx20M -Xmn10M -XX:+PrintGCDetails -XX:SurvivorRatio=8 -XX:+UseSerialGC
* -XX:-HandlePromotionFailure (JDK 6 Update 24后已失效,默认打开)
*/
@SuppressWarnings("unused")
public static void testHandlePromotion() {
byte[] allocation1, allocation2, allocation3, allocation4, allocation5, allocation6, allocation7;
allocation1 = new byte[2 * _1MB];
allocation2 = new byte[2 * _1MB];
allocation3 = new byte[2 * _1MB];
allocation1 = null;
allocation4 = new byte[2 * _1MB];
allocation5 = new byte[2 * _1MB];
allocation6 = new byte[2 * _1MB];
allocation4 = null;
allocation5 = null;
allocation6 = null;
allocation7 = new byte[2 * _1MB];
}
public static void main(String[] args) {
// testAllocation();
// testPretenureSizeThreshold();
// testTenuringThreshold();
// testTenuringThreshold2();
testHandlePromotion();
}
}