@@ -48,20 +48,20 @@ Java 堆是垃圾收集器管理的主要区域,因此也被称作**GC 堆(G
48
48
>
49
49
> ``` c++
50
50
> 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
+ > ...
65
65
> }
66
66
>
67
67
> ```
155
155
156
156
` ` `
157
157
158
- ! [堆内存常见分配策略 ](./pictures/jvm垃圾回收/堆内存.png)
159
-
160
158
# ## 1.1 对象优先在 eden 区分配
161
159
162
160
目前主流的垃圾收集器都会采用分代回收算法,因此需要将堆内存分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。
167
165
168
166
` ` ` java
169
167
public class GCTest {
170
-
171
168
public static void main(String[] args) {
172
169
byte[] allocation1, allocation2;
173
170
allocation1 = new byte[30900* 1024];
@@ -180,7 +177,7 @@ public class GCTest {
180
177
! [](./pictures/jvm垃圾回收/25178350.png)
181
178
182
179
添加的参数:` -XX:+PrintGCDetails`
183
- ! [](./pictures/jvm垃圾回收/10317146 .png)
180
+ ! [](./pictures/jvm垃圾回收/run-with-PrintGCDetails .png)
184
181
185
182
运行结果 (红色字体描述有误,应该是对应于 JDK1.7 的永久代):
186
183
@@ -244,12 +241,12 @@ public class GCTest {
244
241
> size_t total = 0 ;
245
242
> uint age = 1 ;
246
243
> 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++ ;
253
250
> }
254
251
> uint result = age < MaxTenuringThreshold ? age : MaxTenuringThreshold;
255
252
> ...
@@ -270,7 +267,7 @@ public class GCTest {
270
267
271
268
上面的说法已经在《深入理解 Java 虚拟机》第三版中被改正过来了。感谢 R 大的回答:
272
269
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)
274
271
275
272
** 总结:**
276
273
@@ -298,8 +295,6 @@ public class GCTest {
298
295
299
296
堆中几乎放着所有的对象实例,对堆垃圾回收前的第一步就是要判断哪些对象已经死亡(即不能再被任何途径使用的对象)。
300
297
301
- ! [](./pictures/jvm垃圾回收/11034259.png)
302
-
303
298
# ## 2.1 引用计数法
304
299
305
300
给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加 1;当引用失效,计数器就减 1;任何时候计数器为 0 的对象就是不可能再被使用的。
@@ -323,18 +318,33 @@ public class ReferenceCountingGc {
323
318
324
319
# ## 2.2 可达性分析算法
325
320
326
- 这个算法的基本思想就是通过一系列的称为 ** “GC Roots”** 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的。
321
+ 这个算法的基本思想就是通过一系列的称为 ** “GC Roots”** 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的,需要被回收。
322
+
323
+ 下图中的 ` Object 6 ~ Object 10` 之间虽有引用关系,但它们到 GC Roots 不可达,因此为需要被回收的对象。
327
324
328
- ! [可达性分析算法 ](./pictures/jvm垃圾回收/72762049 .png)
325
+ ! [可达性分析算法](./pictures/jvm垃圾回收/jvm-gc-roots .png)
329
326
330
- 可作为 GC Roots 的对象包括下面几种:
327
+ ** 哪些对象可以作为 GC Roots 呢? **
331
328
332
329
- 虚拟机栈(栈帧中的本地变量表)中引用的对象
333
330
- 本地方法栈(Native 方法)中引用的对象
334
331
- 方法区中类静态属性引用的对象
335
332
- 方法区中常量引用的对象
336
333
- 所有被同步锁持有的对象
337
334
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
+
338
348
# ## 2.3 再谈引用
339
349
340
350
无论是通过引用计数法判断对象引用数量,还是通过可达性分析法判断对象的引用链是否可达,判定对象的存活都与“引用”有关。
@@ -369,19 +379,6 @@ JDK1.2 以后,Java 对引用的概念进行了扩充,将引用分为强引
369
379
370
380
特别注意,在程序设计中一般很少使用弱引用与虚引用,使用软引用的情况较多,这是因为** 软引用可以加速 JVM 对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出(OutOfMemory)等问题的产生** 。
371
381
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
-
385
382
# ## 2.5 如何判断一个常量是废弃常量?
386
383
387
384
运行时常量池主要回收的是废弃的常量。那么,我们如何判断一个常量是废弃常量呢?
@@ -410,8 +407,6 @@ JDK1.2 以后,Java 对引用的概念进行了扩充,将引用分为强引
410
407
411
408
# # 3 垃圾收集算法
412
409
413
- ! [垃圾收集算法分类](./pictures/jvm垃圾回收/垃圾收集算法.png)
414
-
415
410
# ## 3.1 标记-清除算法
416
411
417
412
该算法分为“标记”和“清除”阶段:首先标记出所有不需要回收的对象,在标记完成后统一回收掉所有没有被标记的对象。它是最基础的收集算法,后续的算法都是对其不足进行改进得到。这种垃圾收集算法会带来两个明显的问题:
@@ -445,8 +440,6 @@ JDK1.2 以后,Java 对引用的概念进行了扩充,将引用分为强引
445
440
446
441
# # 4 垃圾收集器
447
442
448
- ! [垃圾收集器分类](./pictures/jvm垃圾回收/垃圾收集器.png)
449
-
450
443
** 如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。**
451
444
452
445
虽然我们对各个收集器进行比较,但并非要挑选出一个最好的收集器。因为直到现在为止还没有最好的垃圾收集器出现,更加没有万能的垃圾收集器,** 我们能做的就是根据具体应用场景选择适合自己的垃圾收集器** 。试想一下:如果有一种四海之内、任何场景下都适用的完美收集器存在,那么我们的 HotSpot 虚拟机就不会实现那么多不同的垃圾收集器了。
0 commit comments