深入理解java虚拟机 第2章(三):OutOfMemoryError异常
2016-09-06 04:03
537 查看
出程序计数器之外,运行时数据区的几块内存区域均会出现OutOfMemoryError异常。
对于该示例代码则是不断产生对象,充斥java堆,
一般遇到堆内存OOM,排查方式:
检查虚拟机堆内存参数(-Xms,-Xmx);
代码上检查是否存在某些对象的生命周期过长,持有状态时间过长,减少程序运行期间内存消耗;
如果线程请求的栈深度大于虚拟机所允许的最大深度,则抛出
StackOverFlowError异常
如果虚拟机在扩展栈时,无法申请到足够内存,则抛出OOM异常。
测试使用-Xss参数减小栈内存容量。
在单线程下,无论是栈帧太大还是栈容量较小,均抛出StaticError异常。
如果是建立多线程导致的内存溢出,在不减少线程数的情况下,只能通过减少最大堆和减少栈容量,来获取更多的线程。
容易造成jvm死机。。反正我测试时是死机了。。
方法区用于存放Class的相关信息(类名,访问修饰符,常量池等。。),对于该区域的测试思路是运行时产生大量的class信息去填满方法区,直到溢出。
1,java堆溢出
java堆的作用是创建实例对象,分配内存。为防止内存越用越少,java使用了GC,垃圾回收机制,将内存中失效的对象内存空间进行回收,如果当java堆中充满大量对象,堆内存空间不够,则会出现OOM异常。对于该示例代码则是不断产生对象,充斥java堆,
一般遇到堆内存OOM,排查方式:
检查虚拟机堆内存参数(-Xms,-Xmx);
代码上检查是否存在某些对象的生命周期过长,持有状态时间过长,减少程序运行期间内存消耗;
2,java栈溢出
栈容量只有参数-Xss设定,对于栈异常:如果线程请求的栈深度大于虚拟机所允许的最大深度,则抛出
StackOverFlowError异常
如果虚拟机在扩展栈时,无法申请到足够内存,则抛出OOM异常。
测试使用-Xss参数减小栈内存容量。
在单线程下,无论是栈帧太大还是栈容量较小,均抛出StaticError异常。
如果是建立多线程导致的内存溢出,在不减少线程数的情况下,只能通过减少最大堆和减少栈容量,来获取更多的线程。
package chapter2; /** * VM Args :-Xss2m //谨慎,以防造成系统假死 * @author admin * */ public class JavaVMStackOOM { public void stackLeakByThread() throws OutOfMemoryError{ while (true) { new Thread(new Runnable() { @Override public void run() { while (true) { // System.out.println("new Thread : " + Thread.currentThread().getName()); } } }).start(); } } public static void main(String[] args) { JavaVMStackOOM oom = new JavaVMStackOOM(); try { oom.stackLeakByThread(); } catch (Throwable e) { System.out.println("new Thread : " + Thread.currentThread().getName()); System.out.println(e.getMessage()); throw e; } } }
容易造成jvm死机。。反正我测试时是死机了。。
3,方法区和运行时常量池溢出
jdk1.7之前存在“永久代”的问题(GC扩展至方法区,可以管理这部分内存,但是永久代有-XX:MaxPermSize上限限制,容易造成内存溢出)package chapter2; import java.util.ArrayList; import java.util.List; /** * VM Args: -XX:PermSize=10m -XX:MaxPermSize=10m * * @author admin * */ public class RuntimeConstantPoolOOM { /** * 方法区存放了类的信息,对于该区域的内存溢出测试可通过创建大量的类填满方法区,直到溢出 * jdk1.7会一直循环下去,因为常量池移除了永久代,不受-XX:MaxPermSize限制 */ private static void intern1() { // 使用list保持对常量池的引用,防止GC回收常量池 List<String> list = new ArrayList<>(); int i = 0; while (true) { list.add(String.valueOf(i++).intern()); System.out.println("list.size : "+list.size()); } } /** * jdk1.6 : intern()把首次遇到的字符串实例化复制到永久代(字符串常量池),返回的也是永久代中字符串实例的引用, * 而由StringBuilder创建的字符串实例在java堆上,返回的不是同一个引用。 * * jdk1.7+: intern()实现不会再复制实例,只是在常量池中记录首次出现的字符串引用, * 因此intern返回的引用和StringBuilder创建的字符串实例是同一个。 */ private static void intern2() { String str1 = new StringBuilder("计算机").append("书籍").toString(); System.out.println(str1.intern() == str1); // java字符串已经在StringBuilder.toString之前存在,常量池已经存在引用,因此返回为false。 String str2 = new StringBuilder("ja").append("va").toString(); System.out.println(str2.intern() == str2); } public static void main(String[] args) { intern1(); // intern2(); } }
方法区用于存放Class的相关信息(类名,访问修饰符,常量池等。。),对于该区域的测试思路是运行时产生大量的class信息去填满方法区,直到溢出。
相关文章推荐
- Ruby中的异常处理代码编写示例
- SQL Server 2005 中使用 Try Catch 处理异常
- MySQL抛出Incorrect string value异常分析
- 浅谈C#中简单的异常引发与处理操作
- 详解C#编程中异常的创建和引发以及异常处理
- 详解JavaScript中的异常处理方法
- java程序中的延时加载异常及解决方案
- 解析Java异常的栈轨迹及其相关方法
- .NET(C#):Emit创建异常处理的方法
- windows7服务器上weblogic启动失败异常解决方法
- 有关ajax的error与后台的异常问题解决
- 深入探讨JAVA中的异常与错误处理
- GO语言异常处理机制panic和recover分析
- 浅谈JAVA 异常对于性能的影响
- 解析C++编程中的bad_cast异常
- Java中的异常测试框架JUnit使用上手指南
- C++ 异常处理 catch(...)介绍
- 详解Oracle自定义异常示例
- php中异常处理方法小结
- PHP中错误与异常的日志记录用法分析