深入理解jvm-2Edition-Java内存区域

Posted Lqblalala

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解jvm-2Edition-Java内存区域相关的知识,希望对你有一定的参考价值。

1、运行时数据区域

  Java虚拟机会将内存区域划分为几个区域,每个区域储存不同类型的数据或承担不同的功能。

  PC,堆-Java堆,栈-虚拟机栈、本地方法栈,方法区、直接内存

  当类被实例化或static方法被调用时,Class文件被加载,关于类的信息储存在方法区里(有了模子)。虚拟机获得了类的相关信息,就可以在Java堆里创建实例对象了。类方法被调用时,便会在Java栈中产生一个栈帧来记录调用的上下文信息,其中局部变量表中保存了指向Java堆中实例对象的引用以便操纵对象。当程序的执行需要调用由其他语言编写的方法时(系统调用等的dll),就需要本地方法栈来服务。

  1.1、程序计数器PC

    下一条指令(字节码)的地址,通过改变PC的值来完成程序状态切换。多线程中,每个线程都有自己的执行流程,因此它们要有自己的程序计数器PC

    执行Native方法时,PC为空(Undefined)。

    没有OutOfMemoryError

  1.2、Java虚拟机栈

    描述Java方法的执行,线程私有,生命周期与线程相同。每个方法被调用时,都会在虚拟机栈中创建一个栈帧(Stack Frame),其中记录了方法执行的上下文信息(局部变量表、操作数栈、动态链    接、方法返回等)。通常所说的堆内存、栈内存一般指Java堆虚拟机栈(局部变量表部分)

    局部变量表存放编译时确定的各种基本数据类型(boolea...、reference、returnAddress),局部变量表所需空间在编译时完成分配,运行期间不会改变。

    栈深度超过最大值时-StackOverflowError;扩展时无法申请到足够内存-OutOfMemoryError。

  1.3、本地方法栈

    虚拟机栈为虚拟机运行Java方法(Java字节码)服务,而本地方法栈为虚拟机使用到的Native方法本地方法)服务。

    什么是本地方法?——"A native method is a Java method whose implementation is provided by non-java code."

    Native Method就是一个java调用非java代码的接口。一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C。

    StackOverflowError,OutOfMemoryError。

  1.4、Java堆

    存放类的实例对象,由所有线程共享。所有的对象实例以及数组都要在堆上分配(数组也是一种对象,不同类型数组会有不同的类)。

    Java堆也是GC的主要管理区域,从垃圾回收角度看,Java堆可以细分为Young Generation(新生代)Old Generation(老年代)

    更加细分可以分为Eden空间From Survivor空间To Survivor空间等。划分的目的只是为了更好的进行垃圾回收。

    逻辑地址连续,物理地址可不连续。

    堆无法扩展时抛出OutOfMemoryError

    关于新生代、老年代、永久代更详细的介绍可参见:https://www.jianshu.com/p/d3a0b4e36c28

  1.5、方法区

    储存被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,由所有线程共享。

    常量池:Class文件一部分,存放编译期生成的各种字面量和符号引用。在类加载后存放入运行时常量池。

    运行时常量池:方法区的一部分,相较于常量池更动态,运行期间也可能有新的常量放入。

  1.6、直接内存

    Direct Memory,不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域。

    NIO中使用的基于Channel和Buffer的IO方式可使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象操纵。避免了在Java堆和Native堆中来回复制,提高了性能。

  1.7、HotSpot对象创建过程

    1、new

    2、在常量池中寻找符号引用

    3、检查符号代表的类是否被加载、解析和初始化过,没有加载则进行加载,找不到Class文件则报ClassNotFind错误

    4、为新生对象分配内存:

      紧缩过的内存采用Bump the Point方式,即指针向空闲区域移动对象大小长度

      内存没有紧缩过,不规整,则采用Free List方式,在记录的空闲块中寻找相应大小的块来分配

      保证内存分配时的线程安全性:

        1、采用同步处理,Compare And Set 加失败重试。

        2、为每个线程预分配单独的空间,线程在其自己的空间上创建对象,但是,线程空间扩容时还是要进行同步处理。

    5、将分配出的空间初始化为0(不包括对象头)

    6、对对象头中信息进行必要设置

    7、执行<init>进行对象初始化

  1.8、对象内存布局

    Header(对象头)、(Instance Data)实例数据、Padding(对其填充)

    Header分为两部分,第一部分存储对象的自身的运行时数据(HashCode、GC分代年龄、锁状态标记等);第二部分是类型指针,指向方法区中类的元数据信息。如果对象是一个数组,则还要有长度信息。

    Instance Data部分存储对象的实例信息,包括它自己的以及继承而来的所有信息。存储顺序受虚拟机分配策略参数和字段在源码中定义顺序影响。

    Padding只起占位符作用,受JVM对对象起始地址的要求影响。

  1.9、对象访问定位

    对象是在堆上创建的,而操作对象需要通过Java栈上的reference数据来进行。

    主流方式有句柄直接指针两种。

    句柄池中的句柄保存了对象实例数据地址以及对象的类型数据(元数据)地址,reference中存储对象句柄的地址即可定位到对象和其元数据。

    采用直接指针时,reference指向对象地址,对象的对象头中的类型指针指向其元数据地址

    句柄的优势是稳定,对象位置移动时,只需改动句柄中的数据,而reference本身不用改变。

    直接指针直接指向对象,因此访问速度快

  1.10、内存区域相关的JVM参数

    Xms20m 堆的最小值设为20m

    Xmx20m 堆的最大值设为20m

    XX:+HeapDumpOnOutOfMemoryError 让虚拟机在出现内存溢出时Dump出当前内存堆转储快照以便时候分析

    Xss128k 设定栈容量为128k

    Xoss128k 设置本地方法栈大小为128k(HotSpot不区分虚拟机栈和本地方法栈,因此该方法无用)

    XX:MaxPermSize 设置方法区最大值

    XX:PermSize 设置方法区大小

    XX:MaxDirectMemorySize 设置最大直接内存大小,如不指定则与Java最大值一样

 

    

    

以上是关于深入理解jvm-2Edition-Java内存区域的主要内容,如果未能解决你的问题,请参考以下文章

深入理解java虚拟机系列:java内存区域与内存溢出异常

深入理解 JVM 的内存区域

深入理解JVM之JVM内存区域与内存分配

深入理解Java内存区域

深入理解JVM--Java 内存区域

深入理解JVM之内存区域