`
jaesonchen
  • 浏览: 309926 次
  • 来自: ...
社区版块
存档分类
最新评论

JVM运行时的数据区 (内存溢出实例)

 
阅读更多

一、概述

运行时数据区是jvm运行时的内存布局,类装载到内存后存放的位置,为执行引擎提供所需指令和数据。运行时数据区包括:堆、栈、方法区、本地方法栈、pc计数器。

接下来会详细介绍各个部分,并介绍直接内存访问和方法区中的常量池,另外对于每个区域可能发生的内存异常用demo做讲解。

二、详细介绍各部分

1、堆

分配运行时产生的对象分配在堆中,但是并不是一定就分配在堆中,随着运行时编译器优化技术的不断进步,逃逸分析和标量替换技术的不断成熟,对象也可以分配到栈中。由于jvm内存是由虚拟机自己分配和回收,不需要开发人员手工干预,所以jvm一般会提供gc功能,但是jvm spec并没有明确要求必须提供。

整个系统只有一个堆,所有的对象分配都在一个逻辑区域。堆是一个多线程共享的区域,所以对堆中对象的访问就需要考虑并发。(jvm有什么措施)。

在jvm的实现中,一般堆都会划分成不同的区域,比如hotspot vm的堆划分为年轻代Eden、中生代Survivor和年老代Tenured区域,根据对象存活的年龄放到不同的区域,区域的大小可以通过参数设置。这些讲到gc的时候会详细讲解。

如果要存放的对象超过了堆的最大大小,则会抛出OutOfMemoryError,简称OOM异常。

2、栈

栈主要是运行时存放在方法调用信息(编译器已确定占用空间的东西怎么体现)的区域,栈是线程私有的,每个线程都有一个栈,线程生而栈生,线程死而栈灭。

栈中内存不需要额外收集,每次方法调用完成后都会自动回收。hospot vm中可以通过-Xss设置栈大小,默认是2m。

每次方法调用,都会产生一个栈帧,方法调用结束都会弹出栈帧。栈帧主要包括局部变量表、操作数栈、帧数据区。帧数据区又包括运行时解析常量池会用到的常量池指针、方法返回信息、指向异常的信息(还有什么)

所有栈占用的内存大小怎么计算?虚拟机整个的内存-堆内存-方法区内存-pc计数器-本地方法栈?

如果栈可以动态的在堆中分配,则如果分配不了更多的栈内存的时候会抛出OOM异常,否则会抛出StackOverflowError

3、方法区

方法区存class文件加载到内存后的类型信息,如类型描述符、全限定名、接口、字段、方法、属性信息,还有运行时常量池、指向堆中Class实例和ClassLoader实例的信息。另外还存放jit编译产生的本地代码。

方法区在逻辑上是堆的一部分,但是不同的虚拟机有不同的叫法,hotspot叫做non-heap内存,IBM J9和Jrockit则没有这种叫法。hotspot后续版本也会去掉这种叫法,放到native memory的区域

方法区的类型信息也可以卸载,及gc,但是比较严格,像java动态代理生成的代理类经常会被卸载。类型信息要被卸载必须满足这些条件:a、类型所有实例都被回收了 b、Class对象被回收了 c、加载该类的ClassLoader已经被回收 d、还有什么

该区域也会内存溢出,溢出时候抛出OOM异常,如果jar包太多而方法区内存太小或者运行时动态向常量池添加太多东西,就会抛出OOM

4、本地方法栈

执行本地代码的时候用到的栈,hotspot的本地方法栈和栈是同一块区域。

5、pc计数器

指向当前线程将要指向的下一条指令,线程私有的区域,一个线程有一个pc计数器。该区域不会抛出异常。一个字长

6、Direct Memory

java1.4开始提供了NIO,大量读取数据的时候,不用先把数据拷入jvm内存中,直接从os内存读取。所以该区域不占用jvm的堆内存,通过参数-XX:MaxDirectMemorySize设置该区域大小,如果不设置默认跟堆大小一样大。

该区域会抛出OOM异常,何时?

7、常量池

类文件的常量池装载进内存后同样会形成常量池,这是运行时常量池。与类文件常量池不同的是,运行时常量池在使用的时候需要解析,需要把一些entry的符号引用转化成直接引用。

如3提到,此区域在内存不够用时会抛出OOM。

三、例子

1、堆

先创建一个包含大对象的类

Java代码  收藏代码
  1. package com.yymt.jvm;  
  2. public class BigObejct {  
  3.     int[] value;  
  4.     private static final int M1 = 1024 * 1024;  
  5.   
  6.     public BigObejct() {  
  7.         //4 * 1m = 4m  
  8.         this.value = new int[M1];  
  9.     }  
  10. }  

   循环创建类,注释中-Xmx10m是启动时虚拟机参数,最大堆内存是10m

Java代码  收藏代码
  1. package com.yymt.jvm.oom;  
  2.   
  3. import com.yymt.jvm.BigObejct;  
  4.   
  5. public class HeapOOM {  
  6.   
  7.     /** 
  8.      * -Xmx10m 
  9.      * @param args 
  10.      */  
  11.     public static void main(String[] args) {  
  12.         BigObejct[] objs = new BigObejct[20];  
  13.         for(int i = 0;i < 20;i++){  
  14.             objs[i] = new BigObejct();  
  15.         }  
  16.     }  
  17.   
  18. }  

堆溢出,注意此处OutOfMemoryError: Java heap space表明是堆空间溢出

Java代码  收藏代码
  1. Exception in thread "main" java.lang.OutOfMemoryError: Java heap space  
  2.     at com.yymt.jvm.BigObejct.<init>(BigObejct.java:8)  
  3.     at com.yymt.jvm.oom.HeapOOM.main(HeapOOM.java:15)   

  2、栈溢出错误StackOverflowError

Java代码  收藏代码
  1. package com.yymt.jvm.oom;  
  2.   
  3. public class StackOverflow {  
  4.   
  5.     /** 
  6.      * -Xss500k 如果加上此参数则不会出现StackOverflowError 
  7.      * @param args 
  8.      */  
  9.     public static void main(String[] args) {  
  10.         recursiveInvoke(500011);  
  11.     }  
  12.     public static void recursiveInvoke(int cnt,int param1,int param2){  
  13.         if(cnt == 0){  
  14.             return;  
  15.         }  
  16.         int newCnt = cnt - 1;  
  17.         int param3 = param1 + param2;  
  18.         int param4 = param1 - param2;  
  19.         recursiveInvoke(newCnt, param4, param3);  
  20.     }  
  21.   
  22. }  

 不加-Xss参数时候就直接异常了,至少在我的机子上如此;-Xss100k也可以出异常,-Xss500k就正常运行

Java代码  收藏代码
  1. Exception in thread "main" java.lang.StackOverflowError  
  2.     at com.yymt.jvm.oom.StackOverflow.recursiveInvoke(StackOverflow.java:13)  
  3.     at com.yymt.jvm.oom.StackOverflow.recursiveInvoke(StackOverflow.java:19)  
  4. ......  

 3、方法区

3.1、如果系统非常庞大,比如说一个很大的产品,如ERP系统、CRM系统,里边有非常多的jar包,运行期这些jar都要加载到内存中,而如果方法区设置的过小,就会导致OOM。在客户分阶段上线过程中,第一阶段只上部分模块,比如erp系统只上财务模块,第二期上供应链,第三期上hr、制造,如果前期MaxPermSize设置的过小,就会导致后期部署完相关模块运行过程中出现方法区溢出。此处因为系统代码庞大,代码无法上传。注意此处是PermGen space

Java代码  收藏代码
  1. java.lang.OutOfMemoryError: PermGen space  

 3.2、如果动态创建无数的类放到内存中同样会出现这个问题

Java代码  收藏代码
  1. package com.yymt.jvm.oom;  
  2.   
  3. import java.lang.reflect.Method;  
  4.   
  5. import net.sf.cglib.proxy.Enhancer;  
  6. import net.sf.cglib.proxy.MethodInterceptor;  
  7. import net.sf.cglib.proxy.MethodProxy;  
  8.   
  9. public class PermGenOOM {  
  10.   
  11.     /** 
  12.      * -XX:MaxPermSize=10m 
  13.      * @param args 
  14.      * @throws InterruptedException 
  15.      */  
  16.     public static void main(String[] args) throws InterruptedException {  
  17.         for (int i = 0; i < 5000000; i++) {  
  18.             Enhancer enhancer = new Enhancer();  
  19.             enhancer.setSuperclass(DynClz.class);  
  20.             enhancer.setUseCache(false);  
  21.             enhancer.setCallback(new MethodInterceptor() {  
  22.                 @Override  
  23.                 public Object intercept(Object arg0, Method arg1,  
  24.                         Object[] arg2, MethodProxy arg3) throws Throwable {  
  25.                     return arg3.invokeSuper(arg0, arg2);  
  26.                 }  
  27.             });  
  28.             enhancer.create();  
  29.             Thread.sleep(20);  
  30.         }  
  31.     }  
  32. }  

 

 异常:

 

Java代码  收藏代码
  1. Caused by: java.lang.OutOfMemoryError: PermGen space  

 4、Direct Memory

Java代码  收藏代码
  1. package com.yymt.jvm.oom;  
  2. import java.lang.reflect.Field;  
  3.   
  4. import sun.misc.Unsafe;  
  5.   
  6. public class DirectMemoryOOM {  
  7.   
  8.     private static final int M1 = 1024 * 1024;  
  9.   
  10.     /** 
  11.      * @param args 
  12.      * @throws IllegalAccessException 
  13.      * @throws IllegalArgumentException 
  14.      * @throws InterruptedException  
  15.      */  
  16.     public static void main(String[] args) throws IllegalArgumentException,  
  17.             IllegalAccessException, InterruptedException {  
  18.   
  19.         Field unsafeFld = Unsafe.class.getDeclaredFields()[0];  
  20.         unsafeFld.setAccessible(true);  
  21.         Unsafe unsafe = (Unsafe) unsafeFld.get(null);  
  22.         for (int i = 0; i < 1000000; i++) {  
  23.             unsafe.allocateMemory(M1);  
  24.             Thread.sleep(2);  
  25.         }  
  26.         Thread.sleep(100000);  
  27.   
  28.     }  
  29.   
  30. }  

这部分内存的使用情况,目前visualvm工具中是检测不到的

Java代码  收藏代码
  1. Exception in thread "main" java.lang.OutOfMemoryError  
  2.     at sun.misc.Unsafe.allocateMemory(Native Method)  
  3.     at com.yymt.jvm.oom.DirectMemoryOOM.main(DirectMemoryOOM.java:24)  
分享到:
评论

相关推荐

    JVM运行时数据区

    ### JVM运行时数据区详解 #### 一、概述 Java虚拟机(JVM)作为Java程序的运行环境,定义了一系列用于程序执行过程中使用的数据区域。这些数据区域在JVM启动时创建,在JVM退出时销毁。其中一些数据区域是线程独立...

    jvm内存溢出

    **JVM内存溢出**是一种常见的运行时错误,指的是程序在执行过程中因为无法获得足够的内存资源而导致的问题。这种问题通常发生在程序尝试分配超出系统可用内存限制的新对象时。了解JVM内存结构是理解内存溢出的关键。...

    内存溢出配置,内存溢出配置

    内存溢出是指程序运行时,申请的内存空间超过了系统能够提供的最大值,导致程序无法继续执行下去的现象。在Java中,主要分为堆内存溢出(Heap Space OutOfMemoryError)和永久代/元空间溢出(PermGen/MethodArea ...

    JVM实战-对象访问与内存溢出异常解析

    4. **掌握方法区和运行时常量池内存溢出异常的测试**:研究如何通过动态生成大量类的方法来填充方法区,最终导致方法区溢出;另外,探索如何通过创建大量的字符串常量来填满运行时常量池,触发溢出异常。 5. **掌握...

    JAVA内存溢出问题总结

    在加载数据时,应该避免一次性加载太多数据,而应该采用分页读取的方式来减少内存的占用。 3、异常时没有加 finally{}来释放某些资源,直接使用 JDBC 是很普遍的事情。 在使用 JDBC 时,应该加 finally{} 来释放...

    内存溢出解决

    当程序运行时分配给它的内存空间不足以支撑其正常运行时,就会出现“内存溢出”(Out of Memory Error,简称OOM)。这通常发生在堆内存或方法区等内存区域耗尽时。对于Java应用而言,了解如何有效识别并解决内存溢出...

    JVM内存模型

    堆内存溢出是一种常见的内存溢出异常,发生在堆内存空间不足以存放新对象实例时。永久区内存溢出相对少见,一般是由于需要加载海量的Class数据,超过了非堆内存的容量导致。栈内存也会溢出,但是更加少见。 为了...

    kettle内存溢出(Java heap space)以及解决方法.docx

    然而,在处理大量数据时,Kettle可能会遇到内存管理问题,导致Java堆空间溢出错误。这种错误通常表现为"Java heap space",意味着Java虚拟机(JVM)分配的内存不足以执行任务。 **Java堆空间的原理** Java堆是Java...

    idea git提交内存溢出后dump文件

    标题中的“idea git提交内存溢出后dump文件”指的是在使用IntelliJ IDEA(简称IDEA)这款流行的Java集成开发环境进行Git操作时遇到了内存溢出的问题,导致系统生成了一个名为`heapDump.hprof`的堆转储文件。内存溢出...

    某应用内存溢出(暨jvm)分析分享

    内存溢出是程序运行时由于分配的内存不足导致无法继续执行的现象,常见于资源管理不当的情况。在Java环境中,JVM负责内存的分配和回收,因此对JVM的深入理解至关重要。 在Java应用中,内存主要分为以下几个区域: 1...

    myeclipse内存溢出问题

    当一个类被加载到JVM中时,它的实例将在这个区域分配内存。堆内存可以在JVM启动时通过-Xms和-Xmx参数来控制其初始大小和最大大小。 2. **非堆内存(Non-heap)**:也被称为永久代(PermGen space),主要用于存储类...

    Sun JVM原理与内存管理

    - **Soft**:软引用,当系统即将发生内存溢出时会被回收。 - **Weak**:弱引用,在下一次GC时就会被回收。 - **Phantom**:虚引用,仅用于跟踪对象被垃圾回收的时间点。 #### 六、分代管理 - **Sun Hotspot 对 ...

    Java内存溢出解决办法

    Java内存溢出问题,全称为Java OutOfMemoryError,是Java开发者经常遇到的运行时异常。内存溢出通常发生在程序运行过程中,系统无法为运行的应用程序分配足够的内存资源,导致程序无法正常执行。理解并解决Java内存...

    jboss内存溢出原因

    1. **PermGen Space(永久代)内存溢出**:当JVM的永久代空间不足时,会抛出`java.lang.OutOfMemoryError: PermGen space`异常。 2. **Heap Space(堆空间)内存溢出**:当JVM的堆空间不足时,会抛出`java.lang....

    java内存溢出解决方案

    Java内存溢出问题通常指的是Java应用程序在运行过程中由于内存分配不当或使用过度导致JVM无法分配更多的内存,从而抛出`java.lang.OutOfMemoryError`异常。本文将深入探讨Java内存溢出的不同类型及其解决方案。 1. ...

    基于HeapAnalyzer456.jar 分析java内存溢出

    非堆内存则包含JVM自身运行所需的内存,如方法区、栈等。 HeapAnalyzer456.jar的使用步骤如下: 1. **生成堆转储文件(Heap Dump)**:当Java应用程序出现内存溢出异常时,可以通过JVM参数设置(如`-XX:+...

    jvm 内存分析文档

    理解JVM内存结构和内存分配机制对于避免内存溢出(OutOfMemoryError)、提升程序性能、减少垃圾回收开销至关重要。开发者应关注内存配置、对象生命周期管理以及适当的垃圾回收策略,以优化应用程序的性能和稳定性。

    一次使用Eclipse Memory Analyzer分析Tomcat内存溢出

    - **自动 Dump**:可以通过 JVM 参数 `--XX:-HeapDumpOnOutOfMemoryError` 来设置 JVM 在出现内存溢出时自动生成 dump 文件。 - **手动 Dump**:使用 `jmap` 命令手动生成 dump 文件。例如,在 Linux 系统中,可以...

Global site tag (gtag.js) - Google Analytics