深入理解java虚拟机第二版(二)java内存区域与内存溢出异常

JVM运行时数据区域;HostSpot虚拟机对象创建(java堆给对象分配内存的方式,解决对象内存分配的线程安全方案,对象内存布局,对象的访问定位,异常实例);

1.JVM运行时数据区域:

  • 方法区(Method Area)

    -- 运行时常量池   

  • 堆(Heap)
  • Java虚拟机栈(Java Virtual Machine Stack)
  • 本地方法栈(Native Method Stack)
  • 程序计数器(Program Counter)

Java虚拟机在执行Java程序的过程中会把它管理的内存划分为若干个不同的数据区域。这些区域都有各自的用途,根据Java虚拟机规范的规定,虚拟机所管理内存会被分为如下图所示的几个区域:

 

2.HostSpot虚拟机对象创建

​ 虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载、解析和初始化过。如果没有,那必须先执行相应的类加载过程。在类加载检查通过后,接下来虚拟机将为新生对象分配内存。对象所需内存的大小在类加载完成后,便可以完全确定,为对象分配空间的任务等同于把一块确定大小的内存从java堆中划分出来。内存分配完成后,虚拟机将要分配到的内存空间都初始化为零值(不包括对象头),如果使用TLAb,这一工作过程也可以提前至TLAB分配时进行。这一步操作,保证了对象的实例字段在java代码中可以不赋是初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。最后在执行new指令后,会接着执行init方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全产生出来。

2.1 java堆给对象分配内存的方式:

  • “指针碰撞(Bump the Pointer)”:假设java中的内存是绝对规整的,所有用过的内存都放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,那所有分配内存就仅仅是把那个指针向空闲空间那边挪动一段与对象大小相等的距离
  • “空闲列表(Free List)”:如果java堆中的内存并不是规整的,已经使用的内存和空闲的内存相互交错,那就没有办法进行简单的指针碰撞了,虚拟机就必须维护一个列表,记录上哪些内存块是可以用的,在分配的时候,从列表中找到一块足够大的空间划分对象实例,并更新列表上的记录

2.2 解决对象内存分配的线程安全方案:

  • 一种是对分配内存空间的动作进行同步处理—实际上虚拟机采用CAS配上失败重试的方式保证更新操作的原子性;
  • 另一种是把内存分配的动作按照线程划分在不同的空间中进行,即每个线程在java堆中预先分配一小块内存,称为本地线程分配缓冲区(Thread Loacl Allocation Buffer,TLAB)。

ps:虚拟机是否使用TLAb,可以通过–XX:+/UseTLAB参数来设定

2.3 对象内存布局:

  • 对象头(Object Header)
  • 实例数据(Instance Data)
  • 对齐填充(Padding)

2.4 对象的访问定位:

句柄的方式:

说明:如果使用句柄访问的话,那么Java堆中将会划分出一块儿内存作为句柄池,reference中存储的就是对象的句柄
           地址,而句柄中包含了对象实例数据与类型数据各自的具体的地址信息。

 

直接指针的方式:

说明:如果使用直接指针访问,那么Java堆对象的布局中就必须考虑如何放置访问类型数据的具体信息,而reference中
           存储的直接就是对象地址。

句方式柄与直接指针方式的对比: 

使用句柄来访问的最大好处就是reference中存储的是稳定的句柄地址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而reference本身不需要修改;使用直接指针访问方式的最大好处就是速度更快,它节省了一次指针定位的时间开销,由于对象的访问在Java中非常频繁,因此这类开销积少成多后也是一项非常可观的执行成本。

注:就HotSpot而言,它是使用的直接指针的方式进行对象访问的。

2.5 OOM(OutOfMemoryError)异常

1) java堆溢出

/**
 * @description: java堆溢出
 * -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
 * -XX:+HeapDumpOnOutOfMemoryError  --让虚拟机在出现内存溢出的时候Dump出当前的内存堆转储快照
 * @author: shm
 * @time: 2020/9/5 20:18
 */
public class HeapOOM {
    static class OOMObject{

    }

    public static void main(String[] args) {
        List<OOMObject> list = new ArrayList<>();
        while (true){
            list.add(new OOMObject());
        }
    }
}

  

 

文件:java_pid8404.hprof

可以考虑用 JProfiler 工具分析

 

 

 

 

 

 

 

 

 

声明:本文是个人学习笔记,内容来自《深入理解Java虚拟机·JVM高级特性与最佳实践》周志明 与网络文章

posted @ 2020-03-21 11:48  夜未眠shm  阅读(166)  评论(0)    收藏  举报