1.首先我们要知道jvm是什么?他的作用是啥?
jvm即 java Virtual Machine也就是java虚拟机,是一种计算设备的规范,引入java虚拟机后,就可以一次编译在不同平台上到处运行,java语言编译程序只需要生成在java虚拟机上运行的目标代码。
2.那么jvm是什么结构的呢?又是怎么运行的呢?

java虚拟机总的来说由四部分组成:
类加载器:加载类文件到内存
运行时数据区:jvm重点在下面详细解释
执行引擎:也就是解释器,负责解释命令提交操作系统执行
本地接口:融合不同语言为java所用
3.java运行时数据区由哪些部分组成,又各自负责什么工作呢?
java在内存管理机制下,不需要对每个对象的额生命周期负责,不需要手动去及逆行delete或者free操作,不容易出现内存泄漏或者内存溢出问题
- 堆:是jvm管理的内存中最大的一块,是所有线程共享的。作用主要是存放对象实例及数组。需要注意的是对的GC主要管理的区域!GC在后面重点说一下。
- 方法区:与堆一样,也是线程共享的,主要用来存储被jvm加载的类信息,常量,静态变量,即时编译器产生的代码等数据,也被称为 非堆 ,jvm对方法区限制宽松,甚至不对方法区进行垃圾回收,这就导致老版本的jdk中,方法区也被称为永久代。永久代来实现方法区不是个好方法,容易导致内存溢出,于是JDK7开始将原本存放再永久代中的字符串常量池移除,jdk8正式去除永久代,迎来元空间
- 虚拟机栈:线程私有,存储局部变量
- 本地方法栈:本地方法栈的作用和虚拟机栈的作用类似,区别是啥呢,那就是服务对象不同,虚拟机栈服务于java方法(字节码)服务,而本地方法栈则为虚拟机使用提到的native方法服务
- 程序计数器:可以看作线程锁执行的字节码的行号指数器,指向当前线程下一条应该执行的指令,例如条件、循环、异常、跳转等。一个cpu核心,同一时刻只能跑一个线程,线程的cpu时间片用完就会被挂起,等待OS重新分配时间片再继续执行,那么线程如何执行到哪里了呢?就是通过程序计数器来实现的,每个线程各自维护一个私有的程序计数器。
那么问题来了,方法区、永久代、元空间是什么关系呢?
方法区是JVM规范中定义的一个内存区域,用于存储类的元数据等信息。而永久代和元空间都是方法区的具体实现,存在于不同的java版本中。
说到JVM就无法避免GC的问题!那么GC是什么呢?
4.GC是什么呢?
GC及垃圾收集
堆:一个类要创建多少个对象实例,只有在程序运行时才知道,之部分内存的分配和回收是动态的,需要重点关注GC问题!
方法区:运行时才知道每个类占用的内存是多少?甚至可以再运行时动态的创建类,所以需要考虑垃圾回收的问题!
本地方法栈:与线程生命周期一致,分配与回收具有确定性,不太需要考虑回收的问题
虚拟机栈:与线程生命周期一致,分配与回收具有确定性,不太需要考虑回收的问题
程序计数器:占用空间少,不会出现内存溢出问题
4.1哪些对象需要回收呢?
Java JVM基础:一篇文章彻底搞懂GC(JVM运行时数据区、GC回收算法、垃圾回收器)_51CTO博客_说一下 jvm 运行时数据区?
判断对象是否可以回收有两个办法:
引用计数法:在对象中添加一个引用计数器,每引用一次计数器加一,每取消一次引用计数器就减一,当计数器为0时,说明对象不再被引用,此时就可以将对象回收了。这种方式原理简单,较为高效,但是它存在一个严重的问题!无法解决循环引用,若存在循环引用那么将会无法被回收,出现内存泄漏的问题!
可达性分析算法:主流的商用JVM都是通过可达性分析来判断对象是否可以被回收的!

Object 4.5.6虽然存在引用,但是无法到达GCRoot所以可以被回收。
也就是说如果一个对象无法到达GCRoot那么他就是不可达的,就是可以被回收的。
那么GCRoot是什么呢?

4.2 那么我们知道哪些对象可以回收以后,这些对象是如何进行回收的呢?
首先GC的分类有哪些?
minorGC(Young GC)只对新生代进行垃圾回收
Major GC(old GC)只对老年代进行垃圾回收
Mixed GC 混合GC,只有部分垃圾回收器支持
Fukk GC 堆整个java堆和方法去进行垃圾回收,是耗时最久的GC
其次回收的时间。啥时候进行回收?
不同的垃圾回收器不同,一般新生代在eden区用尽后触发GC
4.3垃圾回收器和垃圾回收算法
垃圾回收器通过特定的算法实现对象的回收和内存清理
那么下面我们就要堆垃圾回收器和垃圾回收算法进行分类和整理了!
首先说说垃圾回收器吧:
说到垃圾回收器就无法避免一个“分代收集”的理论,因为目前的分代收集都遵循“分代收集”理论,分代收集可以提升GC的效率。
分代收集基于三个假说:
弱分代假说:绝大多树对象都是朝生夕死,在进行业务计算后就会被回收
强分代假说:熬过越多GC的对象,越难被回收。基于统计学,经过多次GC仍然存在的对象,可以假定下次不会被回收,将该对象移到老年代,减少回收频率,让GC去处理效率更高的新生代。
跨代引用假说:存在互相引用关系的两个对象倾向于同时生存或同时消亡。(那么如果真的存在跨代引用咋办呢?)
那么如果真的存在跨代引用咋办呢?这是个问题,总不能因为新生代引用老年代就总去扫描老年代,这就很浪费资源了!
JVM当然考虑到了这一点,所以就引入了一个新的概念,那就是记忆集(Remembered Set),通过在新生代创建记忆集的数据结构避免新生代扫描时将老年代也扫描。
分代收集理论有意将需要回收的空间分为新生代和老年代,新生代和老年代也就适用于不同的垃圾回收算法,不同的代也有不同的垃圾回收器适合,所以一般来说JVM需要两个垃圾收集器配合使用
垃圾回收算法:
标记清除法:标记需要被回收的对象,标记完成后统一清除。
标记复制法:为了解决标记清除法产生的随便空间,将内存划分为两块,每次只使用其中的一块,将存活对象复制到另一块上,然后将当前区域全部清除,需要额外的老年代进行分配担保
标记整理法:标记过程与标记清除算法一样,但是标记整理法不会直接清除标记对象,而是将存活对象都向内存区域的一端移动,然后直接清除掉边界外的内存空间。
垃圾回收器:

总的来说大致分为三种:
串行:单线程收集,用户线程暂停
并行:对线程收集,用户线程暂停
并发:用户线程和GC线程同时运行
而实际使用中的搭配如下

我们根据新生代和老年代分别介绍一下这些垃圾收集器:
新生代
Serial:最基础也是最早的垃圾收集器,进开启一个线程完成垃圾回收。使用标记复制算法
Parallel Scavenge:JDK8默认的垃圾收集器,多线程的新生代收集器,使用标记复制算法 ,
ParNew:Serial的多线程版,出现是为了配合老年代的CMS一起进行工作,使用标记复制算法
老年代
Serial Old:单线程独占式垃圾回收器,可以和大多新生代垃圾回收器配合使用,也可以作为CMS的备用收集器,使用标记整理法
Parallel Old:多线程并行独占式垃圾收集器,出现是为了配合Parallel Scavenge一起使用的,使用标记整理法
CMS(Concurrent Mark Sweep):使用标记清理算法,浮动垃圾本次GC无法回收,当浮动垃圾超出CMS预留的内存时回收失败,这是就需要Serial Old进行回收,并且会产生很多内存碎片。可能会存在漏标和错标的情况,解决方案就是原始快照(按开始扫描的对象图快照进行扫描)和增量更新(新的引用记录记录下来。重新扫描)。CMS实现的原理时三色标记:1 所有对象标记为白色 2 将GC Roots直接关联的对象置为灰色 3 遍历灰色对象的所有引用,自身置为黑色 引用置为灰色 4 重复3操作 5黑色对象存活 ,白色对象回收
混合收集器
G1收集器:垃圾优先的收集器,JDK7正式使用,JDK9默认使用,出现是为了替代CMS收集器,G1最大的变化就是分代概念,它只是逻辑分代,物理结构上已经不再分代,他将java堆划分成多个大小不等的Region,每个Region扮演根据需要扮演Eden区,Survivor区,或者老年代,G1对扮演不同分区的Region使用不同的回收算法进行回收?G1不再像以前一样对整个新生代或老年代进行回收,可以面向堆内任何部分组成回收集进行回收,G1会跟踪每一个Region,计算每个Region的回收价值,再后台维护一个优先级列表,根据用户设置的允许GC停顿的时间来优先回收垃圾最多的Region
ZGC收集器:JDK11加入。它同样是基于Region的内存布局形式,最大的特点就是采用了着色指针技术来标记对象。以往记录GC或jvm本身使用的数据时需要再对象头增加额外的字段来进行记录,而ZGC可以直接把标记信息记录再对象的引用指针上。
1519

被折叠的 条评论
为什么被折叠?



