之所以学习 jvm ,是因为在学习多线程相关知识时,对 volatile 关键字理解的不够透彻,总有种似懂非懂的感觉。于是通过在网上各种资料的查阅,最终将 volatile 和 jvm 联系上了,本身又对jvm充满好奇,因此踏上了 jvm 的学习之旅。

一、一个问题

一个面试官对面试问题的分析

Q:GC 是在什么时候,对什么东西,做了什么事情?

二、五张图

看了多遍该书以后,个人觉得可以将书中大部分内容通过以下书中的五张图来进行联想

  • 运行时数据区

  • HotSpot 虚拟机的垃圾收集器

  • 双亲委派机制

  • 栈帧

  • JMM

2.1 运行时数据区

  1. 程序计数器可以看作是当前线程所执行的字节码的行号计数器。每个线程都需要有一个独立的程序计数器,以保证线程切换后能恢复到正确的执行位置,属于线程私有的内存。是唯一一个java虚拟机规范中没有任何 OutofMemoryError 情况的区域。

  2. 虚拟机栈描述的是 java 方法执行的内存模型,每个方法在执行的时候都会创建一个栈帧。也是线程私有的,其生命周期与线程相同。在 java 虚拟机规范中,对这个区域规定了两种异常情况:

    • 线程请求的栈深度 > 虚拟机所允许的深度 ==> StackOverflowError

    • 如果虚拟机栈可以动态扩展,那么扩展时无法申请到足够的内存 ==> OutofMemoryError

  3. 本地方法栈与虚拟机栈发挥的作用是相似的,只是虚拟机栈为虚拟机执行 java 方法服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。也会抛出上面的两种异常。

  4. 是被所有线程共享的一块内存区域,在虚拟机启动时创建,唯一目的就是存放对象实例。java 虚拟机规范中的描述是:所有的对象实例以及数组都要在堆上分配但是随着JIT编译器的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化发生,所有的对象都分配在堆上也渐渐变得不是那么“绝对”了。如果堆中没有内存完成实例分配,并且堆也无法在扩展时,将会产生 OutofMemoryError。

    • 从 GC 角度来看,由于现在收集器基本都采用分代收集算法,堆还可以细分为:新生代、老年代

    • 从内存分配角度看,堆中可能划分出多个线程私有的分配缓冲区

  5. 方法区也是被所有线程共享的一块内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

2.2 HotSpot 虚拟机的垃圾收集器

  1. Serial收集器是一个单线程的收集器,在进行垃圾收集时必须暂停其他所有的工作线程,直到它收集结束。新生代采用复制算法。

  2. ParNew收集器 是Serial收集器的多线程版本,第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。新生代采用复制算法。

  3. Parallel Scavenge收集器和ParNew收集器相似,不同点在于其关注的是达到 一个可控制的吞吐量。

    吞吐量 = 运行用户代码时间 /(运行用户代码时间+垃圾收集时间)

    虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。主要适合在后台运算而不

    需要太多交互的任务。

    新生代采用复制算法。注意的参数包括-XX:MaxGCPauseMillis、-XX:GCTimeRatio、-XX:+UseAdaptiveSizePolicy

  4. Serial Old收集器是Serial收集器的老年代版本,使用“标记-整理”算法。

  5. Parallel Old收集器 是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。

  6. CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。基于“标记—清除”算法实现 的,它的运作过程相对于前面几种收集器来说更复杂一些,整个过程分为4个步骤,包括:

    • 初始标记(CMS initial mark)

    • 并发标记(CMS concurrent mark)

    • 重新标记(CMS remark)

    • 并发清除(CMS concurrent sweep)

    其中,初始标记、重新标记这两个步骤仍然需要 “Stop The World” 。初始标记仅仅只是 标记一下 GC Roots 能直接关联到的对象,速度很快,并发标记阶段就是进行 GC RootsTracing 的过程,而重新标记阶段则是为了修正并发标记期间因用户程序继续运作而导致标记产生变 动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。

    由于整个过程中耗时最长的并发标记和并发清除过程收集器线程都可以与用户线程一起工作,所以,从总体上来说,CMS收集器的内存回收过程是与用户线程一起并发执行的。

    缺点有以下三点:

    • 器对CPU资源非常敏感,CMS默认启动的回收线程数是(CPU数量 +3)/4,即当CPU在4个以上时,并发回收时垃圾收集线程不少于25%的CPU资源,并且随着CPU数量的增加而下降

    • 器无法处理浮动垃圾(Floating Garbage),可能出现“Concurrent Mode Failure”失败而导致另一次 Full GC 的产生。由于CMS并发清理阶段用户线程还在运行着,伴随程序运行自然就还会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS无法在当次收集中处理掉它们,只好留待下一次GC时再清理掉。这一部分垃圾就称为“浮动垃圾”。注意参数-XX:CMSInitiatingOccupancyFraction

    • 基于“标记—清除”算法实现意味着收集结束时会有大量空间碎片产生,将会给大对象分配带来很大麻烦,往往会出现老年代还有很大空间剩余,但是无法找到足够大的连续空间来分配当前对象,不得不提前触发一次Full GC。

      注意参数-XX:+UseCMSCompactAtFullCollection、-XX:CMSFullGCsBeforeCompaction

  7. G1收集器的运作大致可划分为以下几个步骤:

    • 初始标记(Initial Marking)

    • 并发标记(Concurrent Marking)

    • 最终标记(Final Marking)

    • 筛选回收(Live Data Counting and Evacuation)

    如果你现在采用的收集器没有出现问题,那就没有任何理由现在去选择G1,如果你的应用追求低停顿,那G1现在已经可以作为一个可尝试的选择,如果你的应用追求吞吐量,那G1并不会为你带来什么特别的好处。

垃圾收集器参数总结:

2.3 双亲委派机制

双亲委派模型的工作过程是:如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载。

2.4 栈帧的概念结构

栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈(Virtual Machine Stack)的栈元素。栈帧存储了方法的局部变量表、操作数栈、动态连接和方法返回地址等信息。每一个方法从调用开始至执行完成的过程,都对应着一个栈帧在虚拟机栈里面从入栈到出栈的过程

  1. 局部变量表是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。

  2. 操作数栈是一个后入先出(Last In First Out,LIFO)栈,当一个方法刚刚开始执行的时候,这个方法的操作数栈是空的,在方法的执行过程中,会有各种字节码指令往操作数栈中写入和提取内容,也就是出栈/入栈操作。

  3. 动态连接,Class 文件的常量池中存有大量的符号引用,一部分将在每一次运行期间转化为直接引用,这部分则称为动态连接。

  4. 方法返回地址 ,一般来说,方法正常退出时,调用者的PC计数器的值可以作为返回地址,栈帧中很可能会保存这个计数器值。而方法异常退出时,返回地址是要通过异常处理器表来确定的,栈帧中一般不会保存这部分信息

2.5 java内存模型

Java内存模型规定了所有的变量都存储在主内存(Main Memory)中(此处的主内存与介绍物理硬件时的主内存名字一样,两者也可以互相类比,但此处仅是虚拟机内存的一部分)。每条线程还有自己的工作内存(Working Memory,可与前面讲的处理器高速缓存类比),线程的工作内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作(读取、赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成。

三、小结

通过 2.1 和 2.2,联想记忆 jvm 的自动内存管理机制,通过 2.3 联想记忆虚拟机类加载机制,通过 2.4 理解虚拟机字节码执行引擎,通过 2.5 学习并发和线程安全相关内容。

四、一个思考

是否能用一个对象在 jvm 中的生命周期把知识点串起来?

读《深入理解java虚拟机》小结的更多相关文章

  1. 深入理解Java虚拟机(第2版) 笔记目录

    本篇为读深入理解Java虚拟机(第2版)一书的笔记目录. Java 运行期数据区 Java 垃圾回收算法 Java 内存分配策略 Java 类文件结构 Java 加载.链接.初始化 Java 类加载器

  2. 《深入理解 java虚拟机》学习笔记

    java内存区域详解 以下内容参考自<深入理解 java虚拟机 JVM高级特性与最佳实践>,其中图片大多取自网络与本书,以供学习和参考.

  3. (1) 深入理解Java虚拟机到底是什么?

    好文转载:http://blog.csdn.net/zhangjg_blog/article/details/20380971 什么是Java虚拟机   作为一个Java程序员,我们每天都在写Java ...

  4. 深入理解Java虚拟机--下

    深入理解Java虚拟机--下 参考:https://www.zybuluo.com/jewes/note/57352 第10章 早期(编译期)优化 10.1 概述 Java语言的"编译期&q ...

  5. 《深入理解Java虚拟机:JVM高级特性与最佳实践》【PDF】下载

    <深入理解Java虚拟机:JVM高级特性与最佳实践>[PDF]下载链接: https://u253469.pipipan.com/fs/253469-230062566 内容简介 作为一位 ...

  6. 深入理解Java虚拟机到底是什么

    摘自:http://blog.csdn.net/zhangjg_blog/article/details/20380971 什么是Java虚拟机 我们都知道Java程序必须在虚拟机上运行.那么虚拟机到 ...

  7. 深入理解Java虚拟机(类文件结构+类加载机制+字节码执行引擎)

    目录 1.类文件结构 1.1 Class类文件结构 1.2 魔数与Class文件的版本 1.3 常量池 1.4 访问标志 1.5 类索引.父索引与接口索引集合 1.6 字段表集合 1.7 方法集合 1 ...

  8. jvm--深入理解java虚拟机 精华总结(面试)(转)

    深入理解java虚拟机 精华总结(面试)(转) 原文地址:http://www.cnblogs.com/prayers/p/5515245.html 一.运行时数据区域 3 1.1 程序计数器 3 1 ...

  9. 2018.4.23 深入理解java虚拟机(转)

    深入理解java虚拟机 精华总结(面试) 一.运行时数据区域 Java虚拟机管理的内存包括几个运行时数据内存:方法区.虚拟机栈.本地方法栈.堆.程序计数器,其中方法区和堆是由线程共享的数据区,其他几个 ...

  10. 《深入理解Java虚拟机》学习笔记

    <深入理解Java虚拟机>学习笔记 一.走近Java JDK(Java Development Kit):包含Java程序设计语言,Java虚拟机,JavaAPI,是用于支持 Java 程 ...

随机推荐

  1. 一张图概括mysql的各种join用法

  2. 就这?一篇文章让你读懂 Spring 事务

    什么是事务 ▲ 百度百科 概括来讲,事务是一个由有限操作集合组成的逻辑单元.事务操作包含两个目的,数据一致以及操作隔离.数据一致是指事务提交时保证事务内的所有操作都成功完成,并且更改永久生效:事务回滚 ...

  3. Tomcat修改最大连接数及查看最大连接数

    一.背景 公司进行安全整改, 技术要求:会话限制:应能够对应用系统的最大并发会话连接数进行限制: 提供凭证:提供对系统最大并发会话连接数进行限制的截图,需要将所有被检查系统中间件配置截图,如果不限制最 ...

  4. sqldbx配置连接Oracle 12C数据库

    本地开发环境: Windows10 64位.Oracle 12C客户端 32位.sqlDBX (32位) =============================================== ...

  5. Python基础之实现界面和代码分离

    第一步:用QT Designer画一个TreeWidget,存为treeview4.ui,这个处理前面TreeWidget那一节讲过,这里不细讲 treeview4.py # -*- coding: ...

  6. Idea快捷键 累积大全

    分类 Editing 这个 Searching/Replcae Navigation Atl +1                                           打开和关闭左侧p ...

  7. MySQL中特别实用的几种SQL语句【转】

    一.插入或替换 如果我们想插入一条新记录(INSERT),但如果记录已经存在,就先删除原记录,再插入新记录. 情景示例:这张表存的每个客户最近一次交易订单信息,要求保证单个用户数据不重复录入,且执行效 ...

  8. 🔥 LeetCode 热题 HOT 100(21-30)

    46. 全排列 思路:典型回溯法 class Solution { public List<List<Integer>> permute(int[] nums) { Linke ...

  9. HttpRunner3源码阅读:2. 模型定义

    models.py 昨天体验的时候我们分别执行了httprunner -h,httprunner startproject demo, httprunner run demo,但是源码中其调用了其他文 ...

  10. vsftpd安装配置

    vsftpd安装配置 vsftpd测试服务器: 192.168.1.191 1.安装: yum provides */vsftpd yum install vsftpd -y 2.匿名用户最基本配置( ...