`

深入理解java虚拟机读书笔记-第二章

    博客分类:
  • JVM
 
阅读更多

第2章 Java内存区域与内存溢出异常

 

===============运行时数据区=================================================

1.运行时数据区包含:方法区、虚拟机栈、本地方法栈、堆、程序计数器。

2.程序计数器是一块较小的内存空间,它可以看做是当前线程执行的字节码的行号指示器。在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。它是“线程私有”内存。如果线程正在执行JAVA方法,计数器记录正在执行的虚拟机字节码指令的地址;如果是NATIVE方法,则为空。

3.JAVA虚拟机栈也是线程私有的,他的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型;每个方法在执行的同时会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。局部变量表存放了编译器可知的各种基本数据类型(boolean,byte,char,short,int,float,long,double)、数据引用类型(reference)和返回地址returnAddress类型(指向一条字节码指令的地址)。

4.局部变量表所需要的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在桢中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。

5.对于JVM STACK,如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverFlowError,如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。

6.本地方法栈和虚拟机栈发挥的作用相似,区别是JVMSTACK为java方法服务,NATIVE METHOD STACK为Native方法服务。

7.Java堆是被所有线程共享的区域,堆分为:新生代、和年老代。在细点新生代分为Eden空间、From Survivor空间、To Survivor空间。Java堆中可能划分出多个线程私有的分配缓冲区(ThreadLocal Allocation Buffer,TLAB)。Java堆可以处于物理上不连续的内存空间。

8.方法区(永久代)是各个线程共享的内存区域,它用来存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。运行时常量池(Runtime COnstant Pool)是方法区的一部分,用于存放编译器生成的各种字面量和符号引用。运行期间也可能将新的常量放入池中,例如String的.intern()方法。

9.直接内存一般不认为是运行时数据区的部分,在JDK中新加入的NIO类,引入了一种基于通道Channel与缓冲区的IO方式,他可以使用Native函数库直接分配堆外内存。

10.虚拟机需要一个new指令时,会去检查这个指令的参数能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载、解析和初始化过。如果没有,那么必须先执行相应的类加载过程。

11.对象所需内存的大小在类加载完成后便可完全确定,为对象分配空间的任务等同于把一块确定大小的内存从Java堆中划分出来。

12.对象使用Reference来定位,reference根据JVM实现常分为两种。(1)句柄,即指向句柄池里的一个地址,句柄池包含对象实例数据指针和对象类型数据指针,句柄池再指向方法区和实例池。(2)实际地址。即指向Java堆中实际对象的地址。

 

=======================常见溢出=================================

堆溢出

1.-xms和-xmx参数用来设置堆大小的扩展范围。

2.溢出代码

List<Object>list=new ArrayList<Object>();
while(true){
   list.add(new Object());
}

 

 虚拟机栈和本地方法栈溢出

1.如果线程请求的栈深度大于虚拟机所允许的最大深度,将会StackOverFlow异常。

2如果虚拟机栈在扩展时无法申请到足够的内存空间,将会抛出OOM异常。

3.溢出代码

   使用-Xss参数减少栈内存的容量。

   定义大量的本地变量,增大此方法中本地变量表的长度。

   

/**
@author zzm
*/

public class JavaVMStackSOF{

  private int stackLength=1;

  public void stackLeak(){
     stackLength++;
     stackLeak();
  }

  public static void main(String[] args) throws Exception{
    JavaVMStackSOF javaVM=new JavaVMStackSOF ();
    javaVM.stackLeak();
}

}

 

注解:1.虽然stackLength是成员变量,但是方法执行时仍然会压栈。

           2.对于递归调用,java没有做尾递归优化。递归方法是不会使用同一栈帧的,每次递归都会压入新的栈帧。

   

方法区和运行时常量池溢出

    例如反复执行string.intern或者通过cglib创建Class

直接内存溢出

   NIO分配内存

 

附录:

http://www.oschina.net/question/12_31996 10个重要JVM参数

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics