@@ -48,20 +48,20 @@ Java 堆是垃圾收集器管理的主要区域,因此也被称作**GC 堆(G
4848>
4949> ``` c++
5050> uint ageTable::compute_tenuring_threshold (size_t survivor_capacity) {
51- > //survivor_capacity是survivor空间的大小
52- > size_t desired_survivor_size = (size_t)((((double)survivor_capacity)* TargetSurvivorRatio)/100);
53- > size_t total = 0;
54- > uint age = 1;
55- > while (age < table_size) {
56- > //sizes数组是每个年龄段对象大小
57- > total += sizes[ age] ;
58- > if (total > desired_survivor_size) {
59- > break;
60- > }
61- > age++;
62- > }
63- > uint result = age < MaxTenuringThreshold ? age : MaxTenuringThreshold;
64- > ...
51+ > //survivor_capacity是survivor空间的大小
52+ > size_t desired_survivor_size = (size_t)((((double)survivor_capacity)* TargetSurvivorRatio)/100);
53+ > size_t total = 0;
54+ > uint age = 1;
55+ > while (age < table_size) {
56+ > //sizes数组是每个年龄段对象大小
57+ > total += sizes[ age] ;
58+ > if (total > desired_survivor_size) {
59+ > break;
60+ > }
61+ > age++;
62+ > }
63+ > uint result = age < MaxTenuringThreshold ? age : MaxTenuringThreshold;
64+ > ...
6565> }
6666>
6767> ```
155155
156156` ` `
157157
158- ! [堆内存常见分配策略 ](./pictures/jvm垃圾回收/堆内存.png)
159-
160158# ## 1.1 对象优先在 eden 区分配
161159
162160目前主流的垃圾收集器都会采用分代回收算法,因此需要将堆内存分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。
167165
168166` ` ` java
169167public class GCTest {
170-
171168 public static void main(String[] args) {
172169 byte[] allocation1, allocation2;
173170 allocation1 = new byte[30900* 1024];
@@ -180,7 +177,7 @@ public class GCTest {
180177! [](./pictures/jvm垃圾回收/25178350.png)
181178
182179添加的参数:` -XX:+PrintGCDetails`
183- ! [](./pictures/jvm垃圾回收/10317146 .png)
180+ ! [](./pictures/jvm垃圾回收/run-with-PrintGCDetails .png)
184181
185182运行结果 (红色字体描述有误,应该是对应于 JDK1.7 的永久代):
186183
@@ -244,12 +241,12 @@ public class GCTest {
244241> size_t total = 0 ;
245242> uint age = 1 ;
246243> while (age < table_size) {
247- > // sizes数组是每个年龄段对象大小
248- > total += sizes[age];
249- > if (total > desired_survivor_size) {
250- > break;
251- > }
252- > age++ ;
244+ > // sizes数组是每个年龄段对象大小
245+ > total += sizes[age];
246+ > if (total > desired_survivor_size) {
247+ > break;
248+ > }
249+ > age++ ;
253250> }
254251> uint result = age < MaxTenuringThreshold ? age : MaxTenuringThreshold;
255252> ...
@@ -270,7 +267,7 @@ public class GCTest {
270267
271268上面的说法已经在《深入理解 Java 虚拟机》第三版中被改正过来了。感谢 R 大的回答:
272269
273- ! [](https://my-blog-to-use.oss-cn-beijing.aliyuncs.com/2020-8/b48228c2-ac00-4668-a78f-6f221f8563b5 .png)
270+ ! [](./pictures/jvm垃圾回收/rf-hotspot-vm-gc .png)
274271
275272** 总结:**
276273
@@ -298,8 +295,6 @@ public class GCTest {
298295
299296堆中几乎放着所有的对象实例,对堆垃圾回收前的第一步就是要判断哪些对象已经死亡(即不能再被任何途径使用的对象)。
300297
301- ! [](./pictures/jvm垃圾回收/11034259.png)
302-
303298# ## 2.1 引用计数法
304299
305300给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加 1;当引用失效,计数器就减 1;任何时候计数器为 0 的对象就是不可能再被使用的。
@@ -323,18 +318,33 @@ public class ReferenceCountingGc {
323318
324319# ## 2.2 可达性分析算法
325320
326- 这个算法的基本思想就是通过一系列的称为 ** “GC Roots”** 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的。
321+ 这个算法的基本思想就是通过一系列的称为 ** “GC Roots”** 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的,需要被回收。
322+
323+ 下图中的 ` Object 6 ~ Object 10` 之间虽有引用关系,但它们到 GC Roots 不可达,因此为需要被回收的对象。
327324
328- ! [可达性分析算法 ](./pictures/jvm垃圾回收/72762049 .png)
325+ ! [可达性分析算法](./pictures/jvm垃圾回收/jvm-gc-roots .png)
329326
330- 可作为 GC Roots 的对象包括下面几种:
327+ ** 哪些对象可以作为 GC Roots 呢? **
331328
332329- 虚拟机栈(栈帧中的本地变量表)中引用的对象
333330- 本地方法栈(Native 方法)中引用的对象
334331- 方法区中类静态属性引用的对象
335332- 方法区中常量引用的对象
336333- 所有被同步锁持有的对象
337334
335+ ** 对象可以被回收,就代表一定会被回收吗?**
336+
337+ 即使在可达性分析法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑阶段”,要真正宣告一个对象死亡,至少要经历两次标记过程;可达性分析法中不可达的对象被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行 ` finalize` 方法。当对象没有覆盖 ` finalize` 方法,或 ` finalize` 方法已经被虚拟机调用过时,虚拟机将这两种情况视为没有必要执行。
338+
339+ 被判定为需要执行的对象将会被放在一个队列中进行第二次标记,除非这个对象与引用链上的任何一个对象建立关联,否则就会被真的回收。
340+
341+ > ` Object` 类中的 ` finalize` 方法一直被认为是一个糟糕的设计,成为了 Java 语言的负担,影响了 Java 语言的安全和 GC 的性能。JDK9 版本及后续版本中各个类中的 ` finalize` 方法会被逐渐弃用移除。忘掉它的存在吧!
342+ >
343+ > 参考:
344+ >
345+ > - [JEP 421: Deprecate Finalization for Removal](https://openjdk.java.net/jeps/421)
346+ > - [是时候忘掉 finalize 方法了](https://mp.weixin.qq.com/s/LW-paZAMD08DP_3-XCUxmg)
347+
338348# ## 2.3 再谈引用
339349
340350无论是通过引用计数法判断对象引用数量,还是通过可达性分析法判断对象的引用链是否可达,判定对象的存活都与“引用”有关。
@@ -369,19 +379,6 @@ JDK1.2 以后,Java 对引用的概念进行了扩充,将引用分为强引
369379
370380特别注意,在程序设计中一般很少使用弱引用与虚引用,使用软引用的情况较多,这是因为** 软引用可以加速 JVM 对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出(OutOfMemory)等问题的产生** 。
371381
372- # ## 2.4 不可达的对象并非“非死不可”
373-
374- 即使在可达性分析法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑阶段”,要真正宣告一个对象死亡,至少要经历两次标记过程;可达性分析法中不可达的对象被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行 ` finalize` 方法。当对象没有覆盖 ` finalize` 方法,或 ` finalize` 方法已经被虚拟机调用过时,虚拟机将这两种情况视为没有必要执行。
375-
376- 被判定为需要执行的对象将会被放在一个队列中进行第二次标记,除非这个对象与引用链上的任何一个对象建立关联,否则就会被真的回收。
377-
378- > ` Object` 类中的 ` finalize` 方法一直被认为是一个糟糕的设计,成为了 Java 语言的负担,影响了 Java 语言的安全和 GC 的性能。JDK9 版本及后续版本中各个类中的 ` finalize` 方法会被逐渐弃用移除。忘掉它的存在吧!
379- >
380- > 参考:
381- >
382- > - [JEP 421: Deprecate Finalization for Removal](https://openjdk.java.net/jeps/421)
383- > - [是时候忘掉 finalize 方法了](https://mp.weixin.qq.com/s/LW-paZAMD08DP_3-XCUxmg)
384-
385382# ## 2.5 如何判断一个常量是废弃常量?
386383
387384运行时常量池主要回收的是废弃的常量。那么,我们如何判断一个常量是废弃常量呢?
@@ -410,8 +407,6 @@ JDK1.2 以后,Java 对引用的概念进行了扩充,将引用分为强引
410407
411408# # 3 垃圾收集算法
412409
413- ! [垃圾收集算法分类](./pictures/jvm垃圾回收/垃圾收集算法.png)
414-
415410# ## 3.1 标记-清除算法
416411
417412该算法分为“标记”和“清除”阶段:首先标记出所有不需要回收的对象,在标记完成后统一回收掉所有没有被标记的对象。它是最基础的收集算法,后续的算法都是对其不足进行改进得到。这种垃圾收集算法会带来两个明显的问题:
@@ -445,8 +440,6 @@ JDK1.2 以后,Java 对引用的概念进行了扩充,将引用分为强引
445440
446441# # 4 垃圾收集器
447442
448- ! [垃圾收集器分类](./pictures/jvm垃圾回收/垃圾收集器.png)
449-
450443** 如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。**
451444
452445虽然我们对各个收集器进行比较,但并非要挑选出一个最好的收集器。因为直到现在为止还没有最好的垃圾收集器出现,更加没有万能的垃圾收集器,** 我们能做的就是根据具体应用场景选择适合自己的垃圾收集器** 。试想一下:如果有一种四海之内、任何场景下都适用的完美收集器存在,那么我们的 HotSpot 虚拟机就不会实现那么多不同的垃圾收集器了。
0 commit comments