侧边栏壁纸
  • 累计撰写 120 篇文章
  • 累计创建 281 个标签
  • 累计收到 11 条评论
标签搜索
隐藏侧边栏

深入理解java虚拟机 之内存管理机制

骐骏
2018-10-25 / 0 评论 / 0 点赞 / 483 阅读 / 0 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2019-02-20,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

第二部分 自动内存管理机制
第二章 java内存区域与内存溢出异常
1. 运行时数据区域
1. 程序计数器
每个线程一个计数器,用来记录程序执行到哪一行代码
2. JAVA虚拟机栈
记录java方法执行逻辑
3. 本地虚拟机
记录本地方法的执行逻辑
4. 堆
被所有线程共享,保存java对象实例,是垃圾回收主要区域
1. 新生代
eden区
from survivor
to survivor
2. 老年代
5. 方法区
被线程共享,用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译的代码
1. java虚拟机规范把方法区描述为堆的一个逻辑部分
2. Hotspot虚拟机把方法区称为“永久代”
1. 这是因为hotspot虚拟机设计团队把GC分代收集扩展到了方法区,或者说使用“永久代”来实现方法区
2. 并非数据进入了方法区就“永久”存在了,这个区域的垃圾回收主要针对常量池的回收和类型的卸载
6. 运行时常量池
运行时常量池是方法区的一部分
1. 该区域用来存储编译期生成的各种字面量和符号引用
2. 运行期间也可以将新常量放入常量池,如使用String的intern方法
7. 直接内存
直接内存不是虚拟机运行时数据区域的一部分,也不是java虚拟机规范中定义的区域
jdk1.4中引入NIO类,引入了基于通道与缓冲区的I/O方式,他可以使用native方法直接分配堆外内存,然后通过一个存储在java堆中的DirectByteBuffer对象作为这块内存的引用进行操作
2. hotspot虚拟机对象探秘
1. 对象创建
1. 当遇到new指令时,首先检查这个指令参数是否能够在常量池中定位到一个类的符号引用,并检查这个符号引用代表的类是否被加载、解析、初始化过。如果没有就要执行类加载过程
2. 类加载通过后,就要为对象分配内存空间
内存分配策略
1. 如果java堆是规整的,一边是已分配的内存,一边是空闲的,中间放着指针作为分界线,那只需要将指针向空闲区域移动与对象相等的大小,这种方法叫做“指针碰撞法”
2. 如果java堆不是规整的,则虚拟机需要维护一个可用空间的列表,在分配的时候则列表中找出一块足够大的空间分配给对象,这种方法叫做“空闲列表法”
为避免线程并发造成的问题
1. 使用同步方式进行分配空间。实际情况中虚拟机使用CAS方式保证分配空间的原子性
2. 使用本地线程分配缓存(TLAB),把内存分配动作按线程划分在不同的空间之中进行,即每个线程先在java堆中预留一小块内存,哪个线程要分配内存就在哪个线程TLAB上分配
3. 内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头)
4. 虚拟机会对对象进行必要的设置(对象头的设置),例如
1. 对象是个类的实例
2. 如何才能找到类的原数据信息
3. 对象的哈希码
4. 对象GC分代年龄
5.执行init方法,将对象按照程序员意愿进行初始化,init执行成功后,对象创建完成
2. 对象内存布局
1. 对象头
2. 实例数据
3. 对齐填充
3. 对象的访问定位
1. 句柄法

            2. 直接指针

第三章 垃圾收集器与内存分配策略
    1. 对象已死吗
        1. 引用计数法
            1. 每当一个地方引用对象时,就在引用计数器上加1,当引用失效时就在-1
            2. 主流虚拟机没有选用引用计数方法的,主要是因为他无法解决对象循环引用的问题
        2. 可达性分析
            1. 选定GC Roots元素,根据“引用链”查找,当一个点对象不和任何一个GC Roots通过引用链连接时,说明对象不可达,可判定为可回收对象
            2.  可作为GC Roots的对象
                1. 虚拟机栈中引用的对象
                2. 方法区中类静态属性引用的对象
                3. 方法区中常量引用的对象
                4. 本地方法栈中JNI引用的对象
        再谈引用
            1. 强引用
                1. 定义:程序代码中普遍存在的,类似于Object obj = new Object();
                2. 只要强引用存在,垃圾收集器就不会收集对象
            2. 软引用
                1. 一些有用但并非必须的对象
                2. 垃圾收集器会把这些对象列入回收范围之中进行二次回收(在内存不足时会回收)
                3.java提供SoftReference类实现软引用
            3. 弱引用
                1. 比软引用更弱一些
                2. 被弱引用关联的对象在下次垃圾回收时一定会被回收
                3. java提供WeakReference类实现弱引用
            4. 虚引用
                1. 又称为幽灵引用或者幻影引用
                2. 是否存在虚引用,完全不会影响对象的生存时间,也无法通过虚引用来获得对象;设置虚引用的唯一目的就是能在这个对象被收集器回收的时候有一个系统通知
                3. java提供PhantomReference类实现虚引用
    2. 生存还是死亡
        1. 即使“不可达的”的对象,也并非“非死不可”
        2. 对象不可达至少要经历两次标记过程
            1. 在进行可达性分析时,如果“不可达”,则会被第一次标记且进行一次筛选
                1. 筛选条件是此对象是否有必要执行finalize()方法
                    1.对象没有覆盖finalize()
                    2. finalize()方法已经被虚拟机执行过一次
            2. 执行完finalize方法,对F-queue中的对象进行第二次标记,如果此时还未被变量引用则被移除“即将回收”集合
                对象可以在finalize方法中实现“自救”,譬如把自己(this关键字)赋值给某个成员变量或者类变量
    3. 垃圾回收算法
        1. 标记-清除
            1.它有两个不足
                1. 标记和清除的过程效率都不高
                2. 会有大量的空间碎片
        2. 标记-复制
            1. 缺点
                1. 需要“浪费”一部分空间
                2. 当存活率较高的时候进行较多的复制操作,效率将变低
            2. 用来回收新生代
        3. 标记-整理
            1. 用来回收老年代
        4. 分代收集算法
    4. hotspot的算法实现
        1. 枚举根节点
            利用OopMap数据结构来实现“直接知道哪些地方存放着对象引用”
        2. 安全点
            1. 安全点是执行GC的“特定位置”
            2. 安全点一般是方法调用、循环跳出、异常跳转等指令位置
            3. 如何在GC时让所有的线程在安全点停顿下来
                1. 抢先中断法
                2. 主动中断法
        3. 安全区域
            线程在执行期间可以进入到安全点,但“不执行”线程(如seelp,blocked状态),需要使用安全区域来进行gc
    5. 垃圾收集器
        1. serial收集器
            1. 发展历史最悠久的收集器
            2. 串行的
            3. 它在垃圾收集时,必须暂停其他所有工作线程(stop the world)
            4. 新生代使用复制算法,老年代使用标记-整理算法
            5. 简单而高效
            6. serial收集器对于client模式下虚拟机是个很好的选择
        2. parNew收集器
            1. “多线程版”的serial收集器
            2.  是许多运行在server模式下虚拟机首选的新生代收集器
            3.使用-XX:+UseConcMarkSweepGC选项后默认的新生代收集器,也可以使用-XX:UseParNewGC强制指定
        3. parallel Scavenge收集器
            1. 是一个新生代收集器
            2. 使用复制算法,并且并行多线程收集
            3. 达到一个可控制的吞吐量
            4. 适用于后台计算而不需要太多交互的任务
        4. serial Old收集器
            1. 是serial收集器的老年代版本
            2. 串行,采用标记-整理算法
            3. 适用于client模式的虚拟机
        5. Parallel old收集器
            1. 是parallel scavenge收集器的老年代版本
            2. parallel scavenge加Parallel old组合适用于注重吞吐量和cpu资源敏感的场景
        6. CMS收集器
            1. 是一种以获取最短停顿时间为目标的收集器
            2. 采用标记-清理算法
            3.收集过程主要包括四个步骤
                1. 初始标记
                    初始标记,只标记和GC Roots直接关联的对象
                2. 并发标记
                    并发标记就是进行GC Roots Tracing的过程,在3个标记阶段中耗时最长,和用户线程同时运行
                3. 重新标记
                    重新标记是为了修正并发标记阶段因用户进程运行造成的对象变动,耗时比初始标记稍长
                4. 并发清除
                    和用户线程同时运行
                1. 初始标记和重新标记会暂停用户进程,
            缺点
                1. 对cpu资源非常敏感
                    当并发标记、并发清除线程占用的cpu资源占比较大时,就会造成用户线程响应缓慢
                2.无法处理浮动垃圾,可能造成“Concurrent model failure”失败造成full GC
                    1. 因为CMS并发清理阶段用户线程还在运行,此时产生的垃圾只能在下次清理
                    2. 同样由于垃圾收集阶段,还需要为用户线程预留足够的空间,在垃圾回收阶段运行用户线程,所以CMS不能像其他收集器等到老年代快满是才进行回收,jdk1.5时,CMS收集器在老年代使用68%空间后就会进行回收,jdk1.6改为98%
                        要是在CMS运行期间空间无法满足用户线程运行,会触发“concurrent mode failure”,这是虚拟机将启动后备预案:临时启用serial old进行老年代垃圾回收
                3. 由于它使用标记-清理算法会出现大量碎片,所以会出现无法满足大对象
                    1. 提供了-xx:UseCMSOmpactFullCollection开关参数,在进行Full GC时进行空间整理,这个开关默认开启
                    2. 提供-XX:CMSFullGCsBeforeCompaction参数,这个参数指定在进行多少次不压缩的Full GC后再进行一次带压缩的(默认值为0,即每次full GC都进行压缩)来减少整理空间消耗过长时间
        7. G1收集器
第四章 虚拟机性能监控与故障处理工具
第五章 调优案例分析与实战
0

评论区