画布 1
Chapter2.内存区域与内存溢出异常
  运行时数据区域
    程序计数器（执行层面）
Program Counter Register
      线程私有
      针对方法内部：字节码调用
      可看做是当前线程执行字节码的行号指示器（简单点说就是控制执行字节码的）
      执行Java方法：记录的是字节码指令的地址
      执行Native方法：记录是空（Undefined）
      内存中唯一没有OOM区域
    虚拟机栈（执行层面）
VM Stack
      线程私有
      Java方法执行时线程的内存模型
        直观上看就是debug调试的堆栈信息
        针对方法之间的调用
        栈帧
Stack Frame
          内容
            局部变量表
            操作数栈
            动态链接
            方法出口
            ......
          没法方法从调用到执行完成，对应一个栈帧在虚拟机中入栈到出栈的过程
        异常
          StackOverflowError
            线程请求栈深度超过虚拟机允许深度
          OutOfMemoryError
            虚拟机栈动态扩展时无法申请到内存
    本地方法栈（执行层面）
Native Method Stack
      与虚拟机栈类似，只不过是为Native方法服务
    Java堆（存放层面）
Heap
      线程共享
      详情可看Chapter3
      异常
        OOM
          堆内无法实例分配，且堆无法扩展
    方法区（存放层面）
Method Area
      线程共享
      堆的一个逻辑部分，别名：Non-Heap
      存储内容
        虚拟机加载的类
        常量
        静态变量
        即时编译器编译后的代码
      之前称“永久代”，JDK1.8中，把存放该数据的区域移到了元空间（MetaSpace）中
        优点
          GC可以像Java堆一样管理这部分内存，省去专门为方法区写内存管理代码
        缺点
          更容易出现内存溢出
          极少数方法（例如String.intern()），在不同虚拟机下有不同的表现
      运行时常量池
        Class文件中的常量池表（Constant Pool Table）
        异常
          OOM
            常量池无法申请到内存时
  直接内存 Direct
    DirectByteBuffer
      占用的是 堆外内存，而是Native直接调用的机器内存。JVM 会在 DirectBuffer 上直接使用 “本地IO操作”（Native I/O Operations）
      好处：避免额外的数据复制，提高IO效率。同时也可以降低GC的工作量。
      场景：需要 容量较大 且 长期使用 的缓存时可考虑使用 DirectBuffer。需要测试确定是否能获得性能提升
      MappedByteBuffer
        继承DirectByteBuffer，可以将文件内容映射到内存
          private final FileDescriptor fd;
    操作
      通过存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作
      DirectBuffer 一般是在 Full GC 时被回收
    异常
      OOM
        不受-Xmx参数控制，设置大小：-XX:MaxDirectMemory=512M
        受物理内存、SWAP分页限制
        如果堆内Xmx满了，物理内存和分页也分配完了，则无法继续申请直接内存
  几个参数含义
    1.used<=committed
2.used、committed <= max (如果定义了max的话)
3.committed表示能保证给JVM使用的内存大小。
4.max和committed可能会随着时间不断变化
5.max内存不能保证内存分配成功，比如如果分配内存后used>committed了，尽管used<max，那也分配失败。
  对象
    创建 new Object()
      问题1：内存划分
        指针碰撞
          如何内存规整，只需要移动指针来给对象分配空间
        空闲列表
          如果内存不规整，虚拟机则需要维护哪些空间是空闲的，分配时找到足够大的空间来分配
        如何选择，是由Java堆是否规整决定，而是否规整是由GC是否带有空间压缩整理（Compact）的能力决定
          因此Serial、ParNew等带有压缩整理，则是指针碰撞
          CMS基于清除Sweep，则是空间列表
      问题2：并发（例如修改对象指针所指向的位置）
        CAS方式保证原子性
        每个线程在Java堆中预先分配一小块内存（本地线程分配缓冲 Thread Local Allocation Buffer ，TLAB），缓冲用完再CAS
        是否开启TLAB：-XX:+/-UseTLAB
      对象头的信息设置
    内存布局
      对象头Header
        Mark Word：存储对象自身的运行时数据，如hashcode、gc分代年龄
        class pointer：类型指针，该指针指向它的类元数据，JVM通过这个指针确定对象是哪个类的实例
          使用64位的指针将浪费大量内存
          为了节约内存可以使用选项+UseCompressedOops开启指针压缩
          还有一些特殊类型的指针JVM不会优化
        array length：如果对象是一个数组，那么对象头还需要有额外的空间用于存储数组的长度
          64位JVM如果开启+UseCompressedOops选项，该区域长度也将由64位压缩至32位
      实例数据Instance Data
        对象真正存储的有效信息
      对齐填充Padding
        非必须，仅占位功能，因为hotspot自动内存管理要求对象起始地址必须是8字节的整数倍，如果对象内容不是8的倍数，则用它填充
    访问定位
      句柄池
        Java堆中划出一块内存存储句柄池，引用中存储的是句柄的地址，而句柄中存储了到对象实例数据和到对象类型数据的指针
        好处：稳定，当对象移动（因为垃圾回收等）时只需要修改句柄中到对象实例的指针即可
        缺点：两次寻址
      直接指针
        reference（对象的引用）中存储的就是对象的地址，此时对象中就要存储到对象类型数据的指针了
        优点：访问对象比较快
        HotSpot主要使用