Java中的垃圾收集
JavaJava执行自动内存管理的过程。 Java程序编译为可以在Java虚拟机或简称 JVM 上运行的字节码。当Java程序在 JVM 上运行时,对象是在堆上创建的,堆是专用于程序的一部分内存。最终,将不再需要某些对象。垃圾收集器找到这些未使用的对象并删除它们以释放内存。
什么是垃圾收集?
在 C/C++ 中,程序员负责对象的创建和销毁。通常,程序员会忽略无用对象的销毁。由于这种疏忽,在某个时刻,可能没有足够的内存来创建新对象,整个程序就会异常终止,导致OutOfMemoryErrors 。
但是在Java中,程序员不需要关心所有不再使用的对象。垃圾收集器销毁这些对象。垃圾收集器的主要目标是通过销毁无法访问的对象来释放堆内存。垃圾收集器是守护线程的最佳示例,因为它始终在后台运行。
Java中的垃圾回收是如何工作的?
Java垃圾收集是一个自动过程。自动垃圾回收是查看堆内存,识别哪些对象正在使用,哪些没有,并删除未使用的对象的过程。使用中的对象或引用的对象意味着程序的某些部分仍然维护指向该对象的指针。程序的任何部分都不再引用未使用或未引用的对象。因此可以回收未引用对象使用的内存。程序员不需要明确标记要删除的对象。垃圾收集实现存在于 JVM 中。
Java垃圾回收中的活动类型
Java中通常发生两种类型的垃圾收集活动。这些都是:
- Minor or incremental Garbage Collection:据说是在年轻代堆内存中的不可达对象被删除时发生的。
- Major or Full Garbage Collection:据说是在Minor Garbage Collection中幸存下来的对象被复制到老年代或永久代堆内存中被移除时发生的。与年轻代相比,垃圾回收在老年代发生的频率较低。
Java中与垃圾收集相关的重要概念
1. 不可达对象:如果一个对象不包含对它的任何引用,则称该对象不可达。另外,请注意,作为隔离岛一部分的对象也是不可访问的。
Integer i = new Integer(4);
// the new Integer object is reachable via the reference in 'i'
i = null;
// the Integer object is no longer reachable.
2. 垃圾收集的资格:如果一个对象不可达,就说它有资格进行 GC(垃圾收集)。在i = null之后,堆区的整数对象 4 适合上图中的垃圾回收。
使对象符合垃圾收集器条件的方法
- 即使程序员不负责销毁无用的对象,但如果不再需要,强烈建议使对象无法访问(因此有资格进行 GC)。
- 通常有四种方法可以使对象符合垃圾回收条件。
- 使参考变量无效
- 重新分配参考变量
- 在方法内部创建的对象
- 孤立岛
请求 JVM 运行 Garbage Collector 的方式
- 一旦我们使一个对象符合垃圾收集的条件,它可能不会立即被垃圾收集器销毁。每当 JVM 运行垃圾收集器程序时,只有对象会被销毁。但是当JVM运行Garbage Collector时,我们不能指望。
- 我们还可以请求 JVM 运行垃圾收集器。有两种方法可以做到:
- 使用System.gc()方法:系统类包含静态方法gc()用于请求 JVM 运行垃圾收集器。
- 使用Runtime.getRuntime().gc()方法: Runtime 类允许应用程序与运行应用程序的 JVM 交互。因此,通过使用它的 gc() 方法,我们可以请求 JVM 运行垃圾收集器。
- 不能保证以上两种方法中的任何一种都会运行垃圾收集器。
- 调用System.gc()等效于调用: Runtime.getRuntime().gc()
定稿
- 就在销毁对象之前,垃圾收集器调用对象的finalize()方法来执行清理活动。一旦finalize()方法完成,垃圾收集器就会销毁该对象。
- finalize()方法存在于具有以下原型的 Object 类中。
protected void finalize() throws Throwable
根据我们的要求,我们可以重写finalize()方法来执行我们的清理活动,比如关闭数据库的连接。
- finalize()方法由垃圾收集器调用,而不是 JVM。但是,垃圾收集器是 JVM 的模块之一。
- 对象类finalize()方法有一个空实现。因此,建议重写finalize()方法来处理系统资源或执行其他清理。
- 对于任何对象, finalize()方法永远不会被多次调用。
- 如果finalize()方法抛出未捕获的异常,则忽略该异常,并终止该对象的终结。
Java中垃圾回收的优点
Java中垃圾收集的优点是:
- 它使Java内存高效,因为垃圾收集器从堆内存中删除了未引用的对象。
- 它由垃圾收集器(JVM 的一部分)自动完成,因此我们不需要额外的努力。
真实世界的例子
让我们举一个真实的例子,我们使用垃圾收集器的概念。
问题:假设你去 GeeksForGeeks 实习,你被告知要编写一个程序来统计在公司工作的员工人数(不包括实习生)。要制作这个程序,您必须使用垃圾收集器的概念。
这是你在公司得到的实际任务:
编写一个程序来创建一个名为 Employee 的类,该类具有以下数据成员。
1. 用于存储分配给每个员工的唯一 ID 的 ID。
2. 员工姓名。
3. 雇员的年龄。
另外,提供以下方法:
- 用于初始化名称和年龄的参数化构造函数。 ID 应在此构造函数中初始化。
- 显示 ID、姓名和年龄的方法 show()。
- 一个方法 showNextId() 来显示下一个员工的 ID。
现在任何不了解Java垃圾收集器的初学者都会编写如下代码:
Java
// Java Program to count number
// of employees working
// in a company
class Employee {
private int ID;
private String name;
private int age;
private static int nextId = 1;
// it is made static because it
// is keep common among all and
// shared by all objects
public Employee(String name, int age)
{
this.name = name;
this.age = age;
this.ID = nextId++;
}
public void show()
{
System.out.println("Id=" + ID + "\nName=" + name
+ "\nAge=" + age);
}
public void showNextId()
{
System.out.println("Next employee id will be="
+ nextId);
}
}
class UseEmployee {
public static void main(String[] args)
{
Employee E = new Employee("GFG1", 56);
Employee F = new Employee("GFG2", 45);
Employee G = new Employee("GFG3", 25);
E.show();
F.show();
G.show();
E.showNextId();
F.showNextId();
G.showNextId();
{ // It is sub block to keep
// all those interns.
Employee X = new Employee("GFG4", 23);
Employee Y = new Employee("GFG5", 21);
X.show();
Y.show();
X.showNextId();
Y.showNextId();
}
// After countering this brace, X and Y
// will be removed.Therefore,
// now it should show nextId as 4.
// Output of this line
E.showNextId();
// should be 4 but it will give 6 as output.
}
}
Java
// Correct code to count number
// of employees excluding interns.
class Employee {
private int ID;
private String name;
private int age;
private static int nextId = 1;
// it is made static because it
// is keep common among all and
// shared by all objects
public Employee(String name, int age)
{
this.name = name;
this.age = age;
this.ID = nextId++;
}
public void show()
{
System.out.println("Id=" + ID + "\nName=" + name
+ "\nAge=" + age);
}
public void showNextId()
{
System.out.println("Next employee id will be="
+ nextId);
}
protected void finalize()
{
--nextId;
// In this case,
// gc will call finalize()
// for 2 times for 2 objects.
}
}
public class UseEmployee {
public static void main(String[] args)
{
Employee E = new Employee("GFG1", 56);
Employee F = new Employee("GFG2", 45);
Employee G = new Employee("GFG3", 25);
E.show();
F.show();
G.show();
E.showNextId();
F.showNextId();
G.showNextId();
{
// It is sub block to keep
// all those interns.
Employee X = new Employee("GFG4", 23);
Employee Y = new Employee("GFG5", 21);
X.show();
Y.show();
X.showNextId();
Y.showNextId();
X = Y = null;
System.gc();
System.runFinalization();
}
E.showNextId();
}
}
Id=1
Name=GFG1
Age=56
Id=2
Name=GFG2
Age=45
Id=3
Name=GFG3
Age=25
Next employee id will be=4
Next employee id will be=4
Next employee id will be=4
Id=4
Name=GFG4
Age=23
Id=5
Name=GFG5
Age=21
Next employee id will be=6
Next employee id will be=6
Next employee id will be=6
现在得到正确的输出:
现在垃圾收集器(gc)将看到 2 个空闲对象。现在要减少 nextId,gc(garbage collector) 只有当我们的程序员在我们的类中重写它时才会调用方法来 finalize()。如前所述,我们必须请求 gc(garbage collector),为此,我们必须在关闭子块的大括号之前编写以下 3 个步骤。
- 设置对 null 的引用(即 X = Y = null;)
- 调用,System.gc();
- 调用,System.runFinalization();
现在计算员工人数的正确代码(不包括实习生)
Java
// Correct code to count number
// of employees excluding interns.
class Employee {
private int ID;
private String name;
private int age;
private static int nextId = 1;
// it is made static because it
// is keep common among all and
// shared by all objects
public Employee(String name, int age)
{
this.name = name;
this.age = age;
this.ID = nextId++;
}
public void show()
{
System.out.println("Id=" + ID + "\nName=" + name
+ "\nAge=" + age);
}
public void showNextId()
{
System.out.println("Next employee id will be="
+ nextId);
}
protected void finalize()
{
--nextId;
// In this case,
// gc will call finalize()
// for 2 times for 2 objects.
}
}
public class UseEmployee {
public static void main(String[] args)
{
Employee E = new Employee("GFG1", 56);
Employee F = new Employee("GFG2", 45);
Employee G = new Employee("GFG3", 25);
E.show();
F.show();
G.show();
E.showNextId();
F.showNextId();
G.showNextId();
{
// It is sub block to keep
// all those interns.
Employee X = new Employee("GFG4", 23);
Employee Y = new Employee("GFG5", 21);
X.show();
Y.show();
X.showNextId();
Y.showNextId();
X = Y = null;
System.gc();
System.runFinalization();
}
E.showNextId();
}
}
输出
Id=1
Name=GFG1
Age=56
Id=2
Name=GFG2
Age=45
Id=3
Name=GFG3
Age=25
Next employee id will be=4
Next employee id will be=4
Next employee id will be=4
Id=4
Name=GFG4
Age=23
Id=5
Name=GFG5
Age=21
Next employee id will be=6
Next employee id will be=6
Next employee id will be=4
相关文章:
- 如何使对象符合Java中的垃圾收集条件?
- Java中的孤立岛
- Java程序的输出 |第 10 组(垃圾收集)
- 如何在Java中找到最大内存、可用内存和总内存?
- JVM 是如何工作的——JVM 架构?