Java 垃圾回收机制

面试必问:Java 垃圾回收机制

介绍

  • 在 C/C++ 中,程序员负责对象的创建和销毁。通常程序员会忽略无用对象的销毁。由于这种疏忽,在某些时候,为了创建新对象,可能没有足够的内存可用,整个程序将异常终止,导致 OutOfMemoryErrors
  • 但是在 Java 中,程序员不需要关心所有不再使用的对象。垃圾回收机制自动销毁这些对象。
  • 垃圾回收机制是守护线程的最佳示例,因为它始终在后台运行。
  • 垃圾回收机制的主要目标是通过销毁无法访问的对象来释放堆内存。

重要条款:

  • 无法访问的对象: 如果一个对象不包含对它的任何引用,则称其为无法访问的对象。另请注意,属于隔离岛的对象也无法访问。
1Integer i = new Integer(4); 2// 新的 Integer 对象可通过 'i' 中的引用访问 3i = null; 4// Integer 对象不再可用。

img

  • 垃圾回收的资格: 如果对象无法访问,则称该对象有资格进行 GC(垃圾回收)。在上图中,在 i = null 之后; 堆区域中的整数对象 4 有资格进行垃圾回收。

使对象符合 GC 条件的方法

  • 即使程序员不负责销毁无用的对象,但如果不再需要,强烈建议使对象不可访问(因此有资格进行 GC)。
  • 通常有四种不同的方法可以使对象适合垃圾回收。
  1. 取消引用变量
  2. 重新分配引用变量
  3. 在方法内部创建的对象
  4. 隔离岛

以上所有带有示例的方法都在单独的文章中讨论:如何使对象符合垃圾收集条件

请求 JVM 运行垃圾收集器的方式

  • 一旦我们使对象符合垃圾收集条件,垃圾收集器可能不会立即销毁它。每当 JVM 运行垃圾收集器程序时,只会销毁对象。但是当 JVM 运行 Garbage Collector 时,我们无法预料。
  • 我们还可以请求 JVM 运行垃圾收集器。有两种方法可以做到:
  1. 使用 *System.gc()* 方法:系统类包含静态方法 gc() 用于请求 JVM 运行垃圾收集器。
  2. 使用 *Runtime.getRuntime().gc()* 方法:运行时类允许应用程序与运行应用程序的 JVM 交互。因此,通过使用其 gc () 方法,我们可以请求 JVM 运行垃圾收集器。
1// 演示请求 JVM 运行垃圾收集器的 Java 程序 2public class Test 3{ 4 public static void main(String[] args) throws InterruptedException 5 { 6 Test t1 = new Test(); 7 Test t2 = new Test(); 8 9 // 取消引用变量 10 t1 = null; 11 12 // 请求 JVM 来运行垃圾收集器 13 System.gc(); 14 15 // 取消引用变量 16 t2 = null; 17 18 // 请求 JVM 来运行垃圾收集器 19 Runtime.getRuntime().gc(); 20 21 } 22 23 @Override 24 // 在垃圾回收之前,在对象上调用一次 finalize 方法 25 protected void finalize() throws Throwable 26 { 27 System.out.println("垃圾收集器调用"); 28 System.out.println("对象垃圾收集:" + this); 29 } 30}

输出:

1垃圾收集器调用 2对象垃圾收集:haiyong.Test@7ad74083 3垃圾收集器调用 4对象垃圾收集:haiyong.Test@7410a1a9

笔记 :

    1. 不能保证以上两种方法中的任何一种都一定会运行垃圾收集器。
    2. 调用 System.gc() 等效于调用:Runtime.getRuntime().gc()

定稿

  • 就在销毁对象之前,垃圾收集器调用对象的 finalize() 方法来执行清理活动。一旦 finalize() 方法完成,垃圾收集器就会销毁该对象。
  • finalize() 方法存在于具有以下原型的 Object 类中。
1protected void finalize() throws Throwable

根据我们的要求,我们可以覆盖 finalize() 方法来执行我们的清理活动,例如关闭数据库连接。

笔记 :

  1. 垃圾收集器而不是 JVM 调用的 finalize() 方法。虽然垃圾收集器是 JVM 的模块之一。
  2. 对象类 finalize() 方法有空实现,因此建议覆盖 finalize() 方法来处理系统资源或执行其他清理。
  3. 对于任何给定的对象,finalize() 方法永远不会被多次调用。
  4. 如果 finalize() 方法抛出未捕获的异常,则忽略该异常并终止该对象的终结。

以上环境博主都是部署在cnaaa服务器上的,感兴趣的伙伴可以自己部署相关的环境进行测试。有关 finalize() 方法的示例,请参阅 Java 程序的输出第十套之垃圾收集

让我们举一个真实的例子,在那里我们使用垃圾收集器的概念。

假设你去字节跳动实习,他们告诉你写一个程序,计算在公司工作的员工人数(不包括实习生)。要制作这个程序,你必须使用垃圾收集器的概念。

这是您在公司获得的实际任务:-

问: 编写一个程序来创建一个名为 Employee 的类,该类具有以下数据成员。 \1. 一个 ID,用于存储分配给每个员工的唯一 ID。 \2. 员工姓名。 \3. 员工年龄。

另外,提供以下方法 -

  1. 用于初始化名称和年龄的参数化构造函数。ID 应在此构造函数中初始化。
  2. 显示 ID、姓名和年龄的方法 show ()。
  3. 显示下一个员工的 ID 的方法 showNextId ()。

现在对垃圾回收机制不了解的初学者可能会这样编写代码:

1//计算在公司工作的员工人数的程序 2 3class Employee 4{ 5 private int ID; 6 private String name; 7 private int age; 8 private static int nextId=1; 9 //它是静态的,因为它在所有对象之间保持通用并由所有对象共享 10 public Employee(String name,int age) 11 { 12 this.name = name; 13 this.age = age; 14 this.ID = nextId++; 15 } 16 public void show() 17 { 18 System.out.println 19 ("Id="+ID+"\nName="+name+"\nAge="+age); 20 } 21 public void showNextId() 22 { 23 System.out.println 24 ("Next employee id will be="+nextId); 25 } 26} 27class UseEmployee 28{ 29 public static void main(String []args) 30 { 31 Employee E=new Employee("GFG1",33); 32 Employee F=new Employee("GFG2",45); 33 Employee G=new Employee("GFG3",25); 34 E.show(); 35 F.show(); 36 G.show(); 37 E.showNextId(); 38 F.showNextId(); 39 G.showNextId(); 40 41 { //这是保留所有实习生的子块。 42 Employee X=new Employee("GFG4",23); 43 Employee Y=new Employee("GFG5",21); 44 X.show(); 45 Y.show(); 46 X.showNextId(); 47 Y.showNextId(); 48 } 49 //这个大括号之后,X 和 Y 将被移除。因此现在它应该显示 nextId 为 4。 50 E.showNextId();//这一行的输出应该是 4,但它会给出 6 作为输出。 51 } 52}

现在获得正确的输出:

现在垃圾收集器(gc)将看到 2 个空闲的对象。现在递减 nextId,gc (garbage collector) 只会在我们的程序员在我们的类中覆盖它时调用方法 finalize () 。如前所述,我们必须请求 gc (garbage collector),为此,我们必须在关闭子块的大括号之前编写以下 3 个步骤。

  1. 将引用设置为 null(即 X = Y = null;)
  2. 调用,System.gc ();
  3. 调用,System.runFinalization ();

现在计算员工人数的正确代码(不包括实习生)

1// 计算不包括实习生的员工人数的正确代码 2class Employee 3{ 4 private int ID; 5 private String name; 6 private int age; 7 private static int nextId=1; 8 //它是静态的,因为它在所有对象之间保持通用并由所有对象共享 9 public Employee(String name,int age) 10 { 11 this.name = name; 12 this.age = age; 13 this.ID = nextId++; 14 } 15 public void show() 16 { 17 System.out.println 18 ("Id="+ID+"\nName="+name+"\nAge="+age); 19 } 20 public void showNextId() 21 { 22 System.out.println 23 ("Next employee id will be="+nextId); 24 } 25 protected void finalize() 26 { 27 --nextId; 28 //在这种情况下,gc 会为 2 个对象调用 finalize() 两次。 29 } 30} 31 32// 它是 Employee 类的右括号 33class UseEmployee 34{ 35 public static void main(String []args) 36 { 37 Employee E=new Employee("GFG1",33); 38 Employee F=new Employee("GFG2",45); 39 Employee G=new Employee("GFG3",25); 40 E.show(); 41 F.show(); 42 G.show(); 43 E.showNextId(); 44 F.showNextId(); 45 G.showNextId(); 46 47 { 48 //这是保留所有实习生的子块。 49 Employee X=new Employee("GFG4",23); 50 Employee Y=new Employee("GFG5",21); 51 X.show(); 52 Y.show(); 53 X.showNextId(); 54 Y.showNextId(); 55 X = Y = null; 56 System.gc(); 57 System.runFinalization(); 58 } 59 E.showNextId(); 60 } 61}
点赞
收藏

评论区

加载中...

相关推荐

js垃圾回收机制原理给你聊的明明白白

前言大多数语言都是提供自动内存管理机制,比如C、Java,JavaScript。自动内存管理机制也就是我们经常听到的垃圾回收机制。好神奇哦,语言会收垃圾,哈哈😄,不过这里的垃圾,可不是家里面的厨余垃圾啥的,而是一些不再使用的变量所占用的内存。我们的js的执行环境会自动对这些垃圾进行回收,也就是释放那些不再使用的变量所占用的内存,收垃圾的过程会按照固定的

java中的GC和内存泄漏

java中的GC1.GC是什么?为什么要有GC? GC是垃圾回收的意思。是指JVM清理不再使用的对象释放内存。垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存.2\.需要GC的内存区域垃圾回收区域:主要针对无用堆对象回

Java系列笔记

Java垃圾回收概况  JavaGC(GarbageCollection,垃圾收集,垃圾回收)机制,是Java与C/C的主要区别之一,作为Java开发者,一般不需要专门编写内存回收和垃圾清理代码,对内存泄露和溢出的问题,也不需要像C程序员那样战战兢兢。这是因为在Java虚拟机中,存在自动内存管理和垃圾清扫机制。概括地说,该机制对JVM(J

Java 内存区域和GC机制

Java垃圾回收概况  JavaGC(GarbageCollection,垃圾收集,垃圾回收)机制,是Java与C/C的主要区别之一,作为Java开发者,一般不需要专门编写内存回收和垃圾清理代码,对内存泄露和溢出的问题,也不需要像C程序员那样战战兢兢。这是因为在Java虚拟机中,存在自动内存管理和垃圾清扫机制。概括地说,该机制对JVM

Java与C++的区别

C和Java的区别:(面试问到这个问题,特来总结一下根据CPrime和ThinkinJava两本书总结而来)尽管Java是基于C的的,但是相比之下,Java是一种更纯粹的面向对象程序设计语言。在Java中,一切都被视为对象。1\.C创建对象之后,需要再使用完将其调用delete方法将其销毁;Java有垃圾回收机制,用

Java虚拟机浅谈——垃圾收集器与内存分配策略

!(https://oscimg.oschina.net/oscnet/1b0b7e8cf19444048f62673420e0de79.gif)在C语言中,有些由内存需要程序员在代码中进行手动回收,但是在Java中,没有这样的声明式操作。有没有人有去想过,Java到底做了什么可以自动进行垃圾回收呢?Java中的垃圾回收,是一点都不需