• 深入理解Java 虚拟机 之自动内存管理


    Java 内存区域与内存溢出异常

    1. 运行时数据区域

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

    (1).程序计数器:较小的内存空间,可以看作时当前线程所执行的字节码的行号指示器。(是唯一一个不会OOM 的区域)

    (2).Java 虚拟栈:存储局部变量、操作数栈、动态链接、方法出口等信息。每一个方法从调用到执行完成,对应一个栈帧在虚拟机栈中入栈和出栈的过程。

    (3).本地方法栈:类似与虚拟机栈,只不过两者执行的服务不一样,前者时虚拟机栈为虚拟机执行Java 方法(也就是字节码)服务,后者为虚拟机使用到的Native 方法服务。

    (4).Java 堆(也叫GC 堆):虚拟机管理的内存中最大的一块,唯一目的就是存放实例对象。

    (5).方法区:存储常量、静态变量等。

    (6).运行时常量池:属于方法区的一部分

    (7).直接内存:既不属于虚拟机也不是Java 虚拟机规范中定义的内存区域。NIO 的时候会用到

    2. HotSpot 虚拟机对象

    进一步了解这些虚拟机内存中的数据的其他细节。包括创建----布局----访问

    (1).对象的创建

    在语言层面上,创建对象(例如:克隆、反序列化)通常仅仅是一个new 关键字而已,而在虚拟机中对象创建要复杂的多,如下图所示:

     

     (2).对象的内存布局:三块区域----对象头、实例数据和对齐填充

    • 对象头

        ①用于存储对象自身的运行时数据:哈希码、GC 分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等;

        ②对象指向它的类元数据的类型指针(但是不一定每个对象都有),如果对象时数组那么对象头中还包括一块用于记录数组长度的数据;

    • 实例数据:是对象真正存储的有效信息,也是程序代码中所定义的哥中类型的字段内容;
    • 对齐填充:不是必然存在,也没有特别的含义,仅仅起着占位符的作用;

    (3).对象的访问定位:原理:栈上的reference 数据来操作堆上的具体对象,有两种方式----句柄和直接指针

    • 句柄:Java 堆中划分一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据和类型数据各自的具体地址信息,如下图所示

       

    • 直接指针:Java 堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,而reference 中存储的直接就是对象地址,如下图所示

       

    • 两种访问的区别:句柄:reference记录的是句柄地址,对象改变时只需要改变句柄中实例数据的指针,不影响reference指向,更稳定;直接指针:直接访问对象,相对句柄访问中间少了一层句柄池,因此更快,但是开销会积少成多,增加执行成本。
    3. OutOfMemoryError 异常

    (1).Java 堆溢出:使用Eclipse Memory Analyzer 分析堆转储快照文件,详细见Eclipse Memory Analyzer,内存泄漏插件,安装使用一条龙

    (2).虚拟机栈和本地方法栈溢出:

    ①如果线程请求的栈深度大于虚拟机所运行的最大深度,将抛出StackOverflowError 异常(内存泄漏)

    ②如果虚拟机在扩展时无法申请到足够的内存空间,则抛出OutOfMemoryError 异常(内存溢出)

    (3).方法区和运行时常量池溢出

    (4).本机直接内存溢出

    4. 总结

    1. 虚拟机中的内存是如何划分

    2. 异常产生的原因

      

  • 相关阅读:
    微信开发-如何自定义页面分享元素
    nginx实现日志按天切割
    JS兼容IE浏览器的方法
    mysql 索引过长1071-max key length is 767 byte
    playframework1.x的eclipse插件开源-playtools
    开放平台-web实现人人网第三方登录
    开放平台-web实现QQ第三方登录
    bash shell执行方式
    pushd和popd
    What do cryptic Github comments mean?
  • 原文地址:https://www.cnblogs.com/hellovoyager1/p/9153434.html
Copyright © 2020-2023  润新知