《深入理解Java虚拟机》读书笔记--第2章 Java 内存区域与内存溢出异常

2.2 运行时数据区域

  1. Java 虚拟机运行时数据区域
    在这里插入图片描述
名称
程序计数器 1.当前线程所执行的字节码行号执行器;2.线程私有
Java虚拟机栈 1.线程私有;2.描述Java方法执行的内存模型;3.每个方法在执行的同时创建一个栈帧来存储局部变量表(基本数据类型、对象引用)、操作数栈、动态链接、方法出口等;4.方法执行看成栈帧入栈出栈过程;5.请求栈深度大于虚拟机允许深度StackOverflowError;6.无法申请足够空间OutOfMemoryError
本地方法栈 1.虚拟机使用到的 Native 方法
Java堆 1.所管理内存最大一块;2.线程共享;3.存放对象的实例;4.所有对象实例和数组都在堆上分配,并不是绝对;5.又称GC堆;6.分代回收算法;7.新生代、老年代;8.Eden、From Survivor[sərˈvaɪvər]、To Survivor;9.堆中没有内存或对无法扩展OutOfMemoryError
方法区 1.线程共享;2.存储已被加载类信息、常量、静态变量、即时编译器编译后的代码等数据 ;3.别名,Non-Heap(非堆);4.可以选择不实现垃圾收集,5.OutOfMemoryError
运行时常量池 1.是方法区一部分;2.Class文件一部分信息存放,编译器生成的各种字面量和符号引用;3.具备动态性;4.OutOfMemoryError
直接内存 1.不是虚拟机运行时数据区一部分,不是Java虚拟机规范定义内存区域;2.JDK1.4新加入NIO,基于通道(Channel)与缓冲区(Buffer)I/O方式,Navite函数库直接分配堆外内存,通过Java堆中 DirectByteBuffer对象作为内存引用,避免Java堆和Native来回复制数据;3.OutOfMemoryError

2.3.1 对象的创建

  1. 内存分配
    1.指针碰撞(指针移动)
    2.空闲列表(维护一个列表)
  2. 并发问题
    1.对分配内存空间的动作进行同步处理,虚拟机采用的CAS配上失败重试的方式保证原子性
    2.内存分配动作安卓线程划分不同的空间之中进行,本地线程分配缓冲(TLAB)
  3. Java对象创建
    1.类加载检查
    2.类加载
    3.分配内存
    4.内存空间初始化为零值
    5.对对象进行设置(信息存放在对象头)
    6.执行init方法

2.3.2 对象的内存布局

  1. HotSpot 虚拟机中,对象内存布局分为三块区域
    1.对象头(Header)
    2.实例数据(Instance Data)
    3.对齐填充(Padding)(并不是必然存在)
  2. 对象头
存储内容 标志位 状态
对象哈希码、对象分代年龄 01 未锁定
指向锁记录指针 00 轻量级锁定
指向重量级锁的指针 10 膨胀(重量级锁定)
空,不需要记录信息 11 GC标志
偏向锁ID、偏向时间戳、对象分代年龄 01 可偏向

2.3.3 对象的访问定位

  1. 访问方式分为两种
    1.句柄池
    2.直接指针

  2. 句柄池
    1.Java堆划分一块内存作为句柄池
    2.reference中存储对象的句柄(包含对象实例数据和类型数据的具体地址信息)地址。
    在这里插入图片描述

  3. 直接指针
    1.reference存储直接就是对象地址
    在这里插入图片描述

方式 优缺点
句柄池 1.reference存储稳定的句柄地址,对象移动,只会改变句柄中的实例数据指针,本身不需要修改
直接指针 1.速度快,节省一次指针定位时间开销

2.4 实战:OutOfMemoryError 异常

  1. 除了程序计数器外,其他运行时区域都有发生的可能。

2.4.1 Java 堆溢出

/*VM Args:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
*-Xms 堆最小值
*-Xmx 堆最大值
*-XX:+HeapDumpOnOutOfMemoryError Dump 出当前内存堆转存快照
*/
public class HeapOOM {
   static class OOMObject {}
   public static void main(String[] args){
       List<OOMObject> list = new ArrayList<>();
       while(true) {
            list.add(new OOMObject()); 
       }   
   }
}
  1. 内存溢出:内存不够了。
  2. 内存泄露:使用完,没及时回收。

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

  1. 使用 -Xss 参数减少栈内存容量
  2. 单线程操作,结论
    1.使用 -Xss 参数减少栈内存容量,StackOverflowError
    2.定义大量本地变量,增大此方法帧中本地变量表的长度,StackOverflowError

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

  1. String.intern():是一个Native 方法,如果字符串常量池中已经包含一个等于此String对象的字符串,则返回代表池中这个字符串的String对象,否则,将此对象包含的字符串添加到常量池中,并且返回此String对象的引用。
  2. 例子
public void test() {
   String str1 = new StringBuilder("计算机").append("软件").toString();
   System.out.println(str1.intern() == str1);
   //1.6 false inter() 首次遇到字符串实例复制到永久代中,返回这个字符串实例引用,而 StringBuilder 创建字符串实例在 Java 堆上,不是同一个引用
   //1.7 true 不会复制实例,intern() 和 StringBuilder 是同一个
  
   String str2 = new StringBuilder("ja").append("va").toString();
   System.out.println(str2.intern() == str2);
   //1.6 false
   //1.7 false 不符合首次出现
}

2.4.4 本地直接内存溢出

  1. DirectMemory 容量可通过 -XX:MaxDirectMemorySize 指定,不指定,默认与 Java 堆最大值(-Xmx指定)一样。
  2. 发生 OOM 之后 Dump 文件很小,程序有直接或间接使用 NIO
发布了40 篇原创文章 · 获赞 15 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/qq_44947117/article/details/104125749