探索深入理解java虚拟机之java内存溢出(OOM)解析(3)
来源:互联网 发布:于正 笑傲江湖 知乎 编辑:程序博客网 时间:2024/05/01 03:39
java内存溢出(OOM)解析
根据java内存的结构,发生内存溢出的地方在于堆、栈、方法区、直接内存。
1、 Java堆溢出(对象太多)
Java堆用于存储对象实例,只要不断的创建对象,并且保证GCRoots到对象之间有可达路径来避免垃圾回收机制清除这些对象,那么在数量到达最大堆的容量限制后就会产生内存溢出异常。
通过参数-XX:+HeapDumpOnOutOfMemoryError可以让虚拟机在出现内存溢出异常时Dump出当前的内存堆转储快照以便事后进行分析。
如果是内存泄漏,可进一步通过工具查看泄漏对象到GC Roots的引用链。于是就能找到泄露对象是通过怎样的路径与GC Roots相关联并导致垃圾收集器无法自动回收它们的。
掌握了泄漏对象的类型信息及GC Roots引用链的信息,就可以比较准确地定位出泄漏代码的位置。
如果不存在泄露,换句话说,就是内存中的对象确实都还必须存活着,那就应当检查虚拟机的堆参数(-Xmx与-Xms),与机器物理内存对比看是否还可以调大,从代码上检查是否存在某些对象生命周期过长、持有状态时间过长的情况,尝试减少程序运行期的内存消耗。
import java.util.ArrayList;import java.util.List;/** * java 堆溢出 * VM Args:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError * @author */ public class Stack {static class OOMObject {}public static void main(String[] args) {List<OOMObject> list = new ArrayList<Stack.OOMObject>();int count = 0;while (true) {list.add(new OOMObject());System.out.println(count++);}}}
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
2、虚拟机栈和本地方法栈溢出
对于HotSpot来说,虽然-Xoss参数(设置本地方法栈大小)存在,但实际上是无效的,栈容量只由-Xss参数设定。
关于虚拟机栈和本地方法栈,在Java虚拟机规范中描述了两种异常:
如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError。
如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常。
public class JVMStackSOF {private int staticLength = 1;private void stackLeak() {staticLength++;stackLeak();}public static void main(String[] args) throws Throwable {JVMStackSOF oom = new JVMStackSOF();try {oom.stackLeak();} catch (Exception e) {System.out.println("stack length:" + oom.staticLength);throw e;}}}
Exception in thread "main" java.lang.StackOverflowError
at demo6.JVMStackSOF.stackLeak(JVMStackSOF.java:8)
在单线程下,无论由于栈帧太大还是虚拟机栈容量太小,当内存无法分配的时候,虚拟机抛出的都是StackOverflowError异常。
如果是多线程导致的内存溢出,与栈空间是否足够大并不存在任何联系,这个时候每个线程的栈分配的内存越大,反而越容易产生内存溢出异常。
解决的时候是在不能减少线程数或更换64为的虚拟机的情况下,就只能通过减少最大堆和减少栈容量来换取更多的线程。
public class javaJVMStackOOM {private void dontStop() {while (true) {}}public void stacLeakByThread() {while (true) {Thread thread = new Thread(new Runnable() {@Overridepublic void run() {dontStop();}});thread.start();}}public static void main(String[] args) throws Throwable {javaJVMStackOOM oom = new javaJVMStackOOM();oom.stacLeakByThread();}}
//i=391
//thread num:391
//Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
3、 方法区和运行时常量池溢出(当运行时常量池过大或者类过多时就会导致方法区溢出)
String.intern()是一个Native方法,它的作用是:如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象;否则,将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。
由于常量池分配在永久代中,可以通过-XX:PermSize和-XX:MaxPermSize限制方法区大小,从而间接限制其中常量池的容量。
import java.util.ArrayList; import java.util.List; /** * VM Args:-XX:PermSize=10M -XX:MaxPermSize=10M * @author */ public class RuntimeConstantPoolOOM { public static void main(String[] args) { // 使用List保持着常量池引用,避免Full GC回收常量池行为 List<String> list = new ArrayList<String>(); // 10MB的PermSize在integer范围内足够产生OOM了 int i = 0; while (true) { list.add(String.valueOf(i++).intern()); } } }
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
at java.lang.String.intern(Native Method)
at baby.oom.RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java:18)
Intern():
JDK1.6 intern方法会把首次遇到的字符串实例复制到永久代,返回的也是永久代中这个字符串实例的引用,而由StringBuilder创建的字符串实例在Java堆上,所以必然不是一个引用。
JDK1.7 intern()方法的实现不会再复制实例,只是在常量池中记录首次出现的实例引用,因此intern()返回的引用和由StringBuilder创建的那个字符串实例是同一个。
public class RunConstantPoolOOM {public static void main(String[] args) {// 计算机软件字符串常量池在StringBuffer.toString();之前没有引用String str1 = new StringBuffer("计算机1").append("软件").toString();System.out.println(str1.intern() == str1);// java字符串常量池在StringBuffer.toString();之前有引用,关键字String str2 = new StringBuffer("dou").append("ble").toString();System.out.println(str2.intern()==str2);}}
1.7 true
false
1.6 false
false
import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * VM Args: -XX:PermSize=10M -XX:MaxPermSize=10M * @author */ public class JavaMethodAreaOOM { public static void main(String[] args) { while (true) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(OOMObject.class); enhancer.setUseCache(false); enhancer.setCallback(new MethodInterceptor() { @Override public Object intercept(Object obj, Method method, Object[] arg, MethodProxy proxy) throws Throwable { // TODO Auto-generated method stub return proxy.invokeSuper(obj, arg); } }); enhancer.create(); } } static class OOMObject { } }
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
at java.lang.String.intern(Native Method)
at baby.oom.RuntimeConstantPoolOOM.main(RuntimeConstantPoolOOM.java:18)
4、 直接内存溢出
虽然使用DerictByteBuffer分配内存也会抛出内存溢出异常,但它抛出异常时并没有真正向操作系统申请分配,而是通过计算得知内存无法分配,于是手动抛出异常,真正申请分配内存的方法是unsafe.allocateMemory()。
import java.lang.reflect.Field; import sun.misc.Unsafe; /** * VM Args:-Xmx20M -XX:MaxDirectMemorySize=10M * @author * Eclipse 默认把这些受访问限制的API设成了ERROR。 解决办法:将Windows->Preferences->Java-Complicer->Errors/Warnings->Deprecated and restricted API,
中的Forbidden references(access rules)设置为Warning,即可以编译通过。 */ public class DirectMemoryOOM { private static final int _1MB = 1024 * 1024; public static void main(String[] args) throws Exception { Field unsafeField = Unsafe.class.getDeclaredFields()[0]; unsafeField.setAccessible(true); Unsafe unsafe = (Unsafe) unsafeField.get(null); while (true) { unsafe.allocateMemory(_1MB); } } }
Exception in thread "main" java.lang.OutOfMemoryError
at sun.misc.Unsafe.allocateMemory(Native Method)
at baby.oom.DirectMemoryOOM.main(DirectMemoryOOM.java:20)
每天努力一点,每天都在进步。
阅读全文
0 0
- 探索深入理解java虚拟机之java内存溢出(OOM)解析(3)
- 探索深入理解java虚拟机之java内存区域解析(1)
- 深入理解java虚拟机(二):java内存溢出实战
- 深入理解java虚拟机(二):java内存溢出实战
- Java内存区域与内存溢出异常OOM——深入理解Java虚拟机
- 探索深入理解java虚拟机之Java内存模型和线程(7)
- 深入理解java虚拟机【内存溢出实例】
- 探索深入理解java虚拟机之垃圾收集(4)
- 深入理解java虚拟机系列(一):java内存区域与内存溢出异常
- Java内存区域和内存溢出异常(深入理解Java虚拟机)
- 深入理解java虚拟机(一)java内存区域与内存溢出异常
- 深入理解Java虚拟机(一)Java内存区域与内存溢出异常
- 深入理解Java虚拟机(第二章):Java内存区域与内存溢出异常
- Java虚拟机OOM之方法区溢出(6)
- 深入理解 Java 虚拟机-Java 内存区域与内存溢出
- 深入理解Java虚拟机----(二)内存区域与内存溢出异常
- 深入理解JAVA虚拟机(一):内存区域与内存溢出异常
- 深入理解JAVA虚拟机(一)内存区域与内存溢出异常
- python库之(random库)
- Lintcode 114.不同的路径
- geoserver安装部署步骤
- 线性表的基本操作及其作用
- 享元模式学习和思考
- 探索深入理解java虚拟机之java内存溢出(OOM)解析(3)
- 撕毁《巴黎协定》,半个硅谷又双炸锅了
- 微信推“闪开发票”功能,真的能解决用户痛点?
- 守护者计划配合深圳警方 打响打击侵犯公民个人信息违法犯罪第一枪
- 荣耀9将于6月12日正式发布,会用上UFS 2.1吗?
- 欢迎使用CSDN-markdown编辑器
- 租赁这头猪,不要趴在共享经济的风口上!
- Android自定义View--使用NestScrolling机制实现一个上下滑动退出Layout
- javascript的>>>