《深入理解java虚拟机》学习笔记2——Java内存溢出实例
2017-05-20 10:52
429 查看
(1).java堆溢出:
Java堆用于存储实例对象,只要不断创建对象,并且保证GC Roots到对象之间有引用的可达,避免垃圾收集器回收实例对象,就会在对象数量达到堆最大容量时产生OutOfMemoryError异常。
想要方便快速地产生堆溢出,要使用如下java虚拟机参数:-Xms10m(最小堆内存为10MB),-Xmx10m(最大堆内存为10MB,最小堆内存和最大堆内存相同是为了避免堆动态扩展),-XX:+HeapDumpOnOutOfMemoryError可以让java**虚拟机在出现内存溢出时产生当前堆内存快照以便进行异常分析。**
方法:通过内存映像工具对dump出来的堆转储快照进行分析,也即是分析是堆溢出还是栈溢出。
根据GC roots的引用链,查找出内存泄漏对象。如果对象必须存活修改堆大小(-xms,xmx)
例子代码如下:
运行一段时间就会发现产生OutOfMemoryError异常,并且产生了堆内存异常dump文件。
(2).java虚拟机栈和本地方法栈溢出:
由于Sun的HotSpot虚拟机不区分java虚拟机栈和本地方法栈,因此对于HotSpot虚拟机来说-Xoss参数(设置本地方法栈大小)虽然存在,但是实际上是无效的,栈容量只能由-Xss参数设定。
由于Java虚拟机栈会出现StackOverflowError和OutOfMemoryError两种异常,所以分别使用两个例子演示这两种情况:
a.java虚拟机栈深度溢出:
单线程的环境下,无论是由于栈帧太大,还是虚拟机栈容量太小,当内存无法再分配的时候,虚拟机总抛出StackOverflowError异常。使用-Xss128k将java虚拟机栈大小设置为128kb,例子代码如下:
运行一段时间后,产生StackOverflowError异常。Java虚拟机栈溢出一般会产生在方法递归调用过多而java虚拟机栈内存不够的情况下。
b.java虚拟机栈内存溢出:
多线程环境下,能够创建的线程最大内存=物理内存-最大堆内存-最大方法区内存,在java虚拟机栈内存一定的情况下,单个线程占用的内存越大,所能创建的线程数目越小,所以在多线程条件下很容易产生java虚拟机栈内存溢出的异常。
使用-Xss 2m参数设置java虚拟机栈内存大小为2MB,例子代码如下:
运行一段时间之后,java虚拟机栈就会因为内存太小无法创建线程而产生OutOfMemoryError。
(3).运行时常量池溢出:
运行时常量池属于方法区的一部分,可以使用-XX:PermSize=10m和-XX:MaxPermSize=10m将永久代最大内存和最小内存设置为10MB大小,并且由于永久代最大内存和最小内存大小相同,因此无法扩展。
String的intern()方法用于检查常量池中如果有等于此String对象的字符串存在,则直接返回常量池中的字符串对象,否则,将此String对象所包含的字符串添加到运行时常量池中,并返回此String对象的引用。因此String的intern()方法特别适合演示运行时常量池溢出,例子代码如下:
运行一段时间,永久代内存不够,运行时常量池因无法再添加常量而产生OutOfMemoryError。
(4).方法区溢出:
运行时常量池是方法区的一部分,他们都属于HotSpot虚拟机中的永久代内存区域。方法区用于存放Class的相关信息,Java的反射和动态代理可以动态产生Class,另外第三方的CGLIB可以直接操作字节码,也可以动态产生Class,实验通过CGLIB来演示,同样使用-XX:PermSize=10m和-XX:MaxPermSize=10m将永久代最大内存和最小内存设置为10MB大小,并且由于永久代最大内存和最小内存大小相同,因此无法扩展。例子代码如下:
运行一段时间之后,永久代内存不够,方法区无法再存放CGLIB创建处理的Class信息,产生方法区OutOfMemoryError。
(5).本机直接内存溢出:
Java虚拟机可以通过参数-XX:MaxDirectMemorySize设定本机直接内存可用大小,如果不指定,则默认与java堆内存大小相同。JDK中可以通过反射获取Unsafe类(Unsafe的getUnsafe()方法只有启动类加载器Bootstrap才能返回实例)直接操作本机直接内存。通过使用-XX:MaxDirectMemorySize=10M,限制最大可使用的本机直接内存大小为10MB,例子代码如下:
当运行一段时间之后,10MB的本机直接内存被分配光,无法在进行直接内存分配时,产生OutOfMemoryError。
Java堆用于存储实例对象,只要不断创建对象,并且保证GC Roots到对象之间有引用的可达,避免垃圾收集器回收实例对象,就会在对象数量达到堆最大容量时产生OutOfMemoryError异常。
想要方便快速地产生堆溢出,要使用如下java虚拟机参数:-Xms10m(最小堆内存为10MB),-Xmx10m(最大堆内存为10MB,最小堆内存和最大堆内存相同是为了避免堆动态扩展),-XX:+HeapDumpOnOutOfMemoryError可以让java**虚拟机在出现内存溢出时产生当前堆内存快照以便进行异常分析。**
方法:通过内存映像工具对dump出来的堆转储快照进行分析,也即是分析是堆溢出还是栈溢出。
根据GC roots的引用链,查找出内存泄漏对象。如果对象必须存活修改堆大小(-xms,xmx)
例子代码如下:
1. public class HeapOOM{ 2. static class OOMObject{ 3. } 4. public static void main(String[] args){ 5. List<OOMObject> list = new ArrayList<OOMObject>(); 6. while(true){ 7. list.add(new OOMObject()); 8. } 9. } 10. }
运行一段时间就会发现产生OutOfMemoryError异常,并且产生了堆内存异常dump文件。
(2).java虚拟机栈和本地方法栈溢出:
由于Sun的HotSpot虚拟机不区分java虚拟机栈和本地方法栈,因此对于HotSpot虚拟机来说-Xoss参数(设置本地方法栈大小)虽然存在,但是实际上是无效的,栈容量只能由-Xss参数设定。
由于Java虚拟机栈会出现StackOverflowError和OutOfMemoryError两种异常,所以分别使用两个例子演示这两种情况:
a.java虚拟机栈深度溢出:
单线程的环境下,无论是由于栈帧太大,还是虚拟机栈容量太小,当内存无法再分配的时候,虚拟机总抛出StackOverflowError异常。使用-Xss128k将java虚拟机栈大小设置为128kb,例子代码如下:
1. public class JavaVMStackOF{ 2. private int stackLength = 1; 3. public void stackLeak(){ 4. statckLength++; 5. stackLeak(); 6. } 7. public static void main(String[] args){ 8. JavaVMStackOF oom = new JavaVMStackOF(); 9. oom.stackLeak(); 10. } 11. }
运行一段时间后,产生StackOverflowError异常。Java虚拟机栈溢出一般会产生在方法递归调用过多而java虚拟机栈内存不够的情况下。
b.java虚拟机栈内存溢出:
多线程环境下,能够创建的线程最大内存=物理内存-最大堆内存-最大方法区内存,在java虚拟机栈内存一定的情况下,单个线程占用的内存越大,所能创建的线程数目越小,所以在多线程条件下很容易产生java虚拟机栈内存溢出的异常。
使用-Xss 2m参数设置java虚拟机栈内存大小为2MB,例子代码如下:
1. public class JavaVMStackOOM{ 2. private void dontStop(){ 3. while(true){ 4. } 5. } 6. public void stackLeakByThread(){ 7. while(true){ 8. Thread t = new Thread(new Runnable(){ 9. public void run(){ 10. dontStop(); 11. } 12. }); 13. t.start(); 14. } 15. } 16. public static void main(String[] args){ 17. JavaVMStackOOM oom = new JavaVMStackOOM(); 18. oom. stackLeakByThread();. 19. } 20. }
运行一段时间之后,java虚拟机栈就会因为内存太小无法创建线程而产生OutOfMemoryError。
(3).运行时常量池溢出:
运行时常量池属于方法区的一部分,可以使用-XX:PermSize=10m和-XX:MaxPermSize=10m将永久代最大内存和最小内存设置为10MB大小,并且由于永久代最大内存和最小内存大小相同,因此无法扩展。
String的intern()方法用于检查常量池中如果有等于此String对象的字符串存在,则直接返回常量池中的字符串对象,否则,将此String对象所包含的字符串添加到运行时常量池中,并返回此String对象的引用。因此String的intern()方法特别适合演示运行时常量池溢出,例子代码如下:
1. public class RuntimeConstantPoolOOM{ 2. public static void main(String[] args){ 3. List<String> list = new ArrayList<String>(); 4. int i = 0; 5. while(true){ 6. list.add(String.valueOf(i++).intern()); 7. } 8. } 9. }
运行一段时间,永久代内存不够,运行时常量池因无法再添加常量而产生OutOfMemoryError。
(4).方法区溢出:
运行时常量池是方法区的一部分,他们都属于HotSpot虚拟机中的永久代内存区域。方法区用于存放Class的相关信息,Java的反射和动态代理可以动态产生Class,另外第三方的CGLIB可以直接操作字节码,也可以动态产生Class,实验通过CGLIB来演示,同样使用-XX:PermSize=10m和-XX:MaxPermSize=10m将永久代最大内存和最小内存设置为10MB大小,并且由于永久代最大内存和最小内存大小相同,因此无法扩展。例子代码如下:
1. public class JavaMethodAreaOOM{ 2. public static void main(String[] args){ 3. while(true){ 4. Enhancer enhancer = new Enhancer(); 5. enhancer.setSuperClass(OOMObject.class); 6. enhancer.setUseCache(false); 7. enhancer.setCallback(new MethodInterceptor(){ 8. public Object intercept(Object obj, Method method, Object[] args, 9. MethodProxy proxy)throws Throwable{ 10. return proxy.invokeSuper(obj, args); 11. } 12. }); 13. enhancer.create(); 14. } 15. } 16. class OOMObject{ 17. } 18. }
运行一段时间之后,永久代内存不够,方法区无法再存放CGLIB创建处理的Class信息,产生方法区OutOfMemoryError。
(5).本机直接内存溢出:
Java虚拟机可以通过参数-XX:MaxDirectMemorySize设定本机直接内存可用大小,如果不指定,则默认与java堆内存大小相同。JDK中可以通过反射获取Unsafe类(Unsafe的getUnsafe()方法只有启动类加载器Bootstrap才能返回实例)直接操作本机直接内存。通过使用-XX:MaxDirectMemorySize=10M,限制最大可使用的本机直接内存大小为10MB,例子代码如下:
1. public class DirectMemoryOOM{ 2. private static final int _1MB = 1024* 1024 * 1024; 3. publc static void main(String[] args) throws Exception{ 4. Field unsafeField = Unsafe.class.getDeclaredFields()[0]; 5. unsafeField.setAccessible(true); 6. Unsafe unsafe = (Unsafe) unsafeField.get(null); 7. while(true){ 8. //unsafe直接想操作系统申请内存 9. unsafe.allocateMemory(_1MB); 10. } 11. } 12. }
当运行一段时间之后,10MB的本机直接内存被分配光,无法在进行直接内存分配时,产生OutOfMemoryError。
相关文章推荐
- 《深入理解java虚拟机》学习笔记2——Java内存溢出实例
- 《深入理解java虚拟机》学习笔记2——Java内存溢出实例
- 《深入理解java虚拟机》学习笔记2——Java内存溢出实例
- 《深入理解java虚拟机》学习笔记2——Java内存溢出实例
- 《深入理解java虚拟机》学习笔记2——Java内存溢出实例
- (转)《深入理解java虚拟机》学习笔记2——Java内存溢出实例
- 《深入理解java虚拟机》学习笔记2——Java内存溢出实例
- 《深入理解java虚拟机》学习笔记9——对象的创建
- 《深入理解Java虚拟机》学习笔记(6)--程序编译与代码优化
- (转)《深入理解java虚拟机》学习笔记5——Java Class类文件结构
- 《深入理解Java虚拟机》学习笔记之最后总结
- 《深入理解java虚拟机》学习笔记5——Java Class类文件结构
- 《深入理解java虚拟机》学习笔记4——Java虚拟机垃圾收集器
- 《深入理解java虚拟机》学习笔记——虚拟机字节码执行
- 《深入理解Java虚拟机——JVM高级特性与最佳实践》学习笔记——自动内存管理机制
- 《深入理解java虚拟机》学习笔记4——Java虚拟机垃圾收集器
- 《深入理解java虚拟机》学习笔记
- (转)《深入理解java虚拟机》学习笔记6——类加载机制
- 《深入理解java虚拟机》学习笔记10——并发编程(二)
- 《深入理解Java虚拟机》学习笔记之垃圾收集器总结