JVM了解

来源:互联网 发布:天狼50软件怎么样 编辑:程序博客网 时间:2024/05/17 21:41

《深入理解Java虚拟机》学习小记一之自动内存管理机制(一)

  • Java内存区域与内存溢出异常
  • 一、概要
  • 二、运行时数据区域
  • 2.1程序计数器
  • 2.2虚拟机栈
  • 2.3本地方法栈
  • 2.4堆
  • 2.5方法区
  • 2.6运行时常量池
  • 三、对象访问
  • 四、OutOfMemoryError异常
  • 4.1Java堆溢出
  • 4.2虚拟机栈溢出
  • 4.3 方法区溢出
  • 4.4 运行时常量池溢出
  • 五、小结
  • Java内存区域与内存溢出异常

    一、概要


    二、运行时数据区域

    Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,如下图所示


    其中虚拟机栈、本地方法栈和程序技术器是线程私有的,方法区和堆是线程共享的.

    2.1程序计数器

    作用:当前线程所执行的字节码的行号指示器

    • 字节码解释器工作时通过改变它的值来选取下一条需要执行的字节码指令
    • 分支、循环、跳转、异常处理和线程恢复都依赖于它

    2.2虚拟机栈

    栈的作用:栈用于存储局部变量表、操作数栈、动态链接和方法出口等信息.

    其中局部变量表用于存放8种基本数据类型(boolean,byte,char,short,int,float,long,double)和reference类型.

    reference类型:

    • 指向对象起始地址的引用指针

    • 指向一个代表对象的句柄

    • 指向一条字节码指令的地址

    可抛出两种异常状况

    • 线程请求的栈深度大于虚拟机所允许的栈深度,抛出StackOverflowError异常

    • 当扩展时无法申请到足够的内存时会抛出OutOfMemoryError异常

    2.3本地方法栈

    与虚拟机栈的作用非常相似.其区别是虚拟机栈执行Java方法服务,而本地方法栈则为虚拟机使用到的Native方法服务

    同时也会抛出StackOverflowError和OutOfMemoryError异常

    2.4堆

    堆的作用:分配所有的对象实例和数组。可以抛出OutOfMemoryError异常。

    2.5方法区

    方法区的作用:用于存储已被虚拟机加载的类信息(Class)、常量(final修饰)、静态变量(static)和即时编译器编译后的代码(code)

    可以抛出OutOfMemoryError异常

    2.6运行时常量池

    属于方法区的一部分,用于存放编译期生成的各种字面量和符号引用(在以后介绍Class结构会讲到),在类加载后存放到方法区的运行时常量池中。可抛出OutOfMemoryError异常

    三、对象访问

    主流的两种访问方式:使用句柄和直接指针。(HotSpot虚拟机就是使用直接指针的访问方式)

    使用句柄访问


    使用直接指针访问


    优缺点比较


    四、OutOfMemoryError异常

    在Java虚拟机规范的描述中,除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生OutOfMemoryError异常的可能.

    下面通过若干实例来验证异常发生的场景.以下代码的开头都注释了执行时所需要设置的虚拟机启动参数,这些参数对实验结果有直接影响,请调试代码的时候不要忽略掉.

    4.1Java堆溢出

    堆里放的是new出来的对象,所以这部分很简单不断的new对象就可以了,但是为了防止对象new出来之后被GC,所以把对象new出来的对象放到一个List中去即可。为了有更好的效果,可以在运行前,调整堆的参数。

    01import java.util.ArrayList;
    02import java.util.List;
    03/**
    04 * VM Args: -Xms20m -Xms20m - XX:+HeapDumpOnOutOfMemoryError
    05 * @author Administrator
    06 *
    07 */
    08public classHeapOOM {
    09       staticclass OOMObject{}
    10       publicstatic void main(String[] args) {
    11            List<OOMObject> list =new ArrayList<HeapOOM.OOMObject>();
    12             while(true){
    13                  list.add(new OOMObject());
    14            }
    15      }
    16}

    运行结果

    java.lang.OutOfMemoryError: Java heap space
    Dumping heap to java_pid3688.hprof ...
    Heap dump file created [364376345 bytes in 15.891 secs]

    4.2虚拟机栈溢出

    • 在单线程的堆中我们不断的让一个成员变量自增,容纳这个变量的单元无法承受这个变量了,就抛出StackOverflowError了。
    • 可以开尽量多的线程,并在每个线程里调用native的方法,就自然会抛出 OutOfMemoryError了
    01/**
    02 * VM Args: - Xss64k
    03 * @author Administrator
    04 *
    05 */
    06public classJavaVMStackSOF {
    07       privateint stackLength = 1;
    08       publicvoid stackLeak(){
    09             stackLength ++;
    10            stackLeak();
    11      }
    12       publicstatic void main(String[] args) throws Throwable {
    13            JavaVMStackSOF oom =new JavaVMStackSOF();
    14             try{
    15                  oom.stackLeak();
    16            }catch(Throwable e){
    17                  System. out.println("Stack length:"+ oom.stackLength);
    18                   throwe;
    19            }
    20      }
    21}

    运行结果
    Stack length:914
    Exception in thread "main" java.lang.StackOverflowError
          at JavaVMStackSOF.stackLeak(  JavaVMStackSOF.java:9)
          at JavaVMStackSOF.stackLeak(  JavaVMStackSOF.java:10)
          at JavaVMStackSOF.stackLeak(  JavaVMStackSOF.java:10)


    4.3 方法区溢出

    可采用增强Class加载的方式。基本思路是运行时产生大量的类去填满方法区,直到溢出。
    借助第三方类库CGLib直接操作字节码运行,生成大量的动态类。
    01import java.lang.reflect.Method;
    02import net.sf.cglib.proxy.MethodInterceptor;
    03import net.sf.cglib.proxy.Enhancer;
    04import net.sf.cglib.proxy.MethodProxy;
    05/**
    06 * VM Args:- XX:PermSize=10m -XX:MaxPermSize=10m
    07 * @author Administrator
    08 *
    09 */
    10public classJavaMethodAreaOOM {
    11       publicstatic void main(String[] args) {
    12             while(true ){
    13                  Enhancer enhancer =new Enhancer();
    14                  enhancer.setSuperclass(OOMObject.class );
    15                  enhancer.setUseCache(false );
    16                  enhancer.setCallback(new MethodInterceptor() {
    17                         @Override
    18                         publicObject intercept(Object obj, Method method, Object[] args,
    19                                    MethodProxy proxy)throws Throwable {
    20                               returnproxy.invoke(obj, args);
    21                        }
    22                  });
    23                  enhancer.create();
    24            }
    25      }
    26       staticclass OOMObject{
    27             
    28      }
    29}

    运行结果
    java.lang.OutOfMemoryError: PermGen space
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
    ... 8 more

    4.4 运行时常量池溢出

    可采用String类的intern方法。
    01import java.util.ArrayList;
    02import java.util.List;
    03/**
    04 * VM Args:- XX:PermSize=10m -XX:MaxPermSize=10m
    05 * @author Administrator
    06 *
    07 */
    08public classRuntimeConstantPoolOOM {
    09       publicstatic void main(String[] args) {
    10            List<String> list =new ArrayList<String>();
    11             inti = 0;
    12             while(true ){
    13                  list.add(String. valueOf(i++).intern());
    14            }
    15      }
    16}

    运行结果
    Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
          at java.lang.String.intern(  Native Method )
          at RuntimeConstantPoolOOM.main(  RuntimeConstantPoolOOM.java:14  )

    五、小结

    主要介绍虚拟机里面的内存是如何划分的,哪部分区域、什么样的代码和操作可能导致内存溢出异常。

    ---------------------------------全文完------------------------------

    摘自《深入理解Java虚拟机》

    原创粉丝点击