JVM 分为三个模块:
1、类加载子系统
2、运行时数据区
3、执行引擎
类加载过程:
1 、加载(将磁盘class文件加载到内存)
2、连接
2.1、验证
验证字节码的正确性
2.2、准备
给类的静态变量分配内存 并赋值默认值
2.3、解析
类装载器装载类所引用的其他类
3、初始化
为类的静态变量赋予正确的初始值,上述的准备阶段为静态变量赋予的是虚拟机默认的初始值,此处赋予的才是程序编写者为变量分配的真正的初始值,执行静态代码块
4、使用
5、卸载
类加载器种类:
启动类加载器(Bootstrap ClassLoader)
负责加载JRE的核心类库,如JRE目标下的rt.jar,charsets.jar等
扩展类加载器(Extension ClassLoader)
负责加载JRE扩展目录ext中jar类包
系统类加载器(Application ClassLoader)
负责加载ClassPath路径下的类包
用户自定义加载器(User ClassLoader)
负责加载用户自定义路径下的类包
如何打破双亲委派?
我们需要继承ClassLoader类,然后重写loadClass和findClass就可以了。
运行时数据区
1、方法区(以前叫永久代,后来为了融合JRockit和HotSpot改为方法区,1.8之后用元空间来实现了方法区)
2、堆(新生代、老年代)
3、虚拟机栈
4、本地方法栈
5、程序计数器
如何判断对象是否可以被回收
1、引用计数法
给对象添加一个引用计数器,每当有一个地方引用,计数器就加1。当引用失效,计数器就减1。任何时候计数器为0的对象就是不可能再被使用的。
2、可达性分析算法(安全点的概念,可以不停机的再安全点挂起可以被挂起的线程)
这个算法的基本思想就是通过一系列的称为”GC Roots“的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连的话,则证明此对象时不可用的。
GC Roots根节点:类加载器、Thread、虚拟机栈的局部变量表、static成员、常量引用、本地方法栈的变量等等
垃圾回收算法:
1、标记-清除
即把死亡对象所占据的内存标记为空闲内存,并记录在一个空闲列表(free list)之中。当需要新建对象时,内存管理模块便会从该空闲列表中寻找空闲内存,并划分给新建的对象。
缺点:
有空间碎片,由于 Java 虚拟机的堆中对象必须是连续分布的,因此可能出现总空闲内存足够,但是无法分配的极端情况。另一个则是分配效率较低。如果是一块连续的内存空,那么我们可以通过指针加法(pointer bumping)来做分配。而对于空闲列表,Java 虚拟机则需要逐个访问列表中的项,来查找能够放入新建对象的空闲内存。
2、标记-压缩
即把存活的对象聚集到内存区域的起始位置,从而留下一段连续的内存空间。这种做法能够解决内存碎片化的问题,但代价是压缩算法的性能开销。
缺点:压缩算法有性能开销
3、复制算法
即把内存区域分为两等分,分别用两个指针 from 和 to 来维护,并且只是用 from 指针指向的内存区域来分配内存。当发生垃圾回收时,便把存活的对象复制到 to 指针指向的内存区域中,并且交换 from 指针和 to 指针的内容。复制这种回收方式同样能够解决内存碎片化的问题,但是它的缺点也极其明显,即堆空间的使用效率极其低下。
垃圾回收器:
新生代:Serial 、Parallel Scavenge、 Parallel New(三个都是标记 - 复制算法)
老年代:
Serial Old (标记-压缩)、 Parallel Old(标记-压缩)、
CMS(标记-清除算法,可以在不用stop-the-world进行并行垃圾回收 但java9中被废弃因为G1横空出世)
G1(标记 - 压缩算法):是一个横跨新生代和老年代的垃圾回收器。它已经打乱了前面所说的堆结构,直接将堆分成极其多个区域。每个区域都可以充当 Eden 区、Survivor 区或者老年代中的一个。可以在不用stop-the-world进行并行垃圾回收
cms垃圾回收器和g1有什么区别?
区别一: 使用范围不一样
CMS收集器是老年代的收集器,可以配合新生代的Serial和ParNew收集器一起使用
G1收集器收集范围是老年代和新生代。不需要结合其他收集器使用
区别二: STW的时间
CMS收集器以最小的停顿时间为目标的收集器。
G1收集器可预测垃圾回收的停顿时间(建立可预测的停顿时间模型)
区别三: 垃圾碎片
CMS收集器是使用“标记-清除”算法进行的垃圾回收,容易产生内存碎片
G1收集器使用的是“标记-整理”算法,进行了空间整合,降低了内存空间碎片。
区别四: 垃圾回收的过程不一样
CMS收集器 G1收集器
1. 初始标记 1.初始标记
2. 并发标记 2. 并发标记
3. 重新标记 3. 最终标记
4. 并发清楚 4. 筛选回收
参考链接文章:
https://www.cnblogs.com/chenpt/p/9803298.html
你们线上应用的 JVM 参数有哪些?
-server -Xmx2g -Xms2g -Xmn256m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX
:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSI
nitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70
-Xms2g JVM初始分配的内存由-Xms指定,默认是物理内存的1/64但小于1G。
-Xmx2g JVM最大分配的内存由-Xmx指定,默认是物理内存的1/4但小于1G。
-XX:PermSize=128m 装载Class信息等基础数据,默认64M
-Xss256k 设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K
-XX:DisableExplicitGC禁止调用代码System.gc()
-XX:+UseConcMarkSweepGC 指定在 Old Generation 使用 concurrent cmark sweep gc,gc thread 和 app thread 并行 ( 在 init-mark 和 remark 时 pause app thread). app pause 时间较短 , 适合交互性强的系统 , 如 web server
-XX:+CMSParallelRemarkEnabled 在使用 UseParNewGC 的情况下 , 尽量减少 mark 的时间
XX:+UseCMSCompactAtFullCollection 在使用 concurrent gc 的情况下 , 防止 memory fragmention, 对 live object 进行整理 , 使 memory 碎片减少
-XX:LargePageSizeInBytes=128m 指定 Java heap 的分页页面大小 , 如 :-XX:LargePageSizeInBytes=128m
-XX:+UseFastAccessorMethods get,set 方法转成本地代码
-XX:+UseCMSInitiatingOccupancyOnly 指示只有在 old generation 在使用了初始化的比例后 concurrent collector 启动收集
XX:CMSInitiatingOccupancyFraction=70 指示在 old generation 在使用了 n% 的比例后 , 启动 concurrent collector, 默认值是 68, 如 :-XX:CMSInitiatingOccupancyFraction=70