个人感觉使用代码来讲解比纯文字更清晰,如下:
// 虚拟机异常一般为 *Error,常见的有 OutOfMemoryError 和 StackOverflowError
public class JVMError {
/**
* JVM options:
* -Xms1M 将堆的最小值设为 1M
* -Xmx1M 将堆的最大值设为 1M
* ps 当堆的最小值和最大值设为一样时,堆的大小是固定的
* <p>
* 在 jdk7 下的运行结果为 java.lang.OutOfMemoryError: Java heap space
* 在 jdk8 下的运行结果为 java.lang.OutOfMemoryError: GC overhead limit exceeded
*/
static class Instance {}
private static void heap() {
List<Instance> list = new LinkedList<>();
// 原理:不断创建新对象,同时使用 list 保持对其的引用(新对象不会被 GC 回收内存),直至堆内存上限
while (true) list.add(new Instance());
}
/**
* JVM options:
* -Xss1K 将栈的大小设为 1K
* ps 在 HotSpot 中虚拟机栈和本地方法栈是合并在一起的
* <p>
* 运行结果为 java.lang.StackOverflowError
*/
private static void stack() {
// 原理:使用没有停止条件的递归,该方法永远不会运行结束,运行直至栈深度上限
stack();
}
/**
* JVM options:
* -Xss1M 将栈的大小设为 1M
* ps Win 下的虚拟机中,Java 的线程是映射到操作系统的内核线程的,所以会导致系统假死;
* ps 理论上这段代码运行后会得到 OutOfMemoryError
* <p>
* 运行结果:死机
*/
private static void stack2() {
// 原理:不断创建新的线程,同时每个线程都保持方法不退出,因为虚拟机栈是线程私有的,将会不断被分配栈内存,直至栈内存上限
while (true) {
Thread thread = new Thread(() -> {
while (true) ;
});
thread.start();
}
}
/**
* JVM options:
* -XX:PermSize=1M 永久代大小为 1M
* -XX:MaxPermSize=1M 永久代最大大小为 1M
* ps String::intern 使用时会在常量池中检查是否含有当前字符串,如果有则返回该字符串的引用,否则将当前字符串存入常量池
* <p>
* 在 jdk7 的运行结果为 java.lang.OutOfMemoryError: PermGen space
* jdk8 的方法区没有使用永久代来实现,而是元空间,所以上述虚拟机参数无效
* <p>
* 这里测试的是 jdk7 之前的方法区的运行时常量池,如果要测试整个方法区,则要不断加载 Class 对象
* 通常的做法是通过 cglib,为某一个类不断创建子类
*/
private static void runtimeConstPool() {
int i = 0;
List<String> list = new LinkedList<>();
// 原理:不断向运行时常量池中添加常量,同时使用 list 保持对常量的引用,直至方法区内存上限
while (true) list.add(String.valueOf(i++).intern());
}
/**
* JVM options:
* -XX:MaxDirectMemorySize=1M 最大直接内存为 1M
* ps 如果不指定 MaxDirectMemorySize,则最大直接内存的值与 -Xmx 一样
* ps 使用 Java nio 中的 DirectByteBuffer 的 Unsafe 实例来分配直接内存
* <p>
* 在 jdk8 下的运行结果为 java.lang.OutOfMemoryError
*/
private static void directMemory() throws IllegalAccessException {
Field field = Unsafe.class.getDeclaredFields()[0];
field.setAccessible(true);
Unsafe unsafe = (Unsafe) field.get(null);
// 原理:不断分配直接内存至上限
while (true) unsafe.allocateMemory(1024 * 1024);
}
}
以上内容为阅读 深入理解Java虚拟机(第2版)后的笔记及对 JDK8 的实践补充。看完这本书后最大的感觉就是,,,再看一遍,很多原来理解不了的知识点就可以看懂了,因为很多内容是前后呼应的。有兴趣的可以去阅读这本书,强推。