1、引用计数法:每个对象有一个引用计数器,新增+1,释放-1,计数为0时可以被回收;
缺点:若存在A、B两个对象,相互引用.那么这两个对象会一直存在,无法被回收
JVM未使用该方法
2、可达性分析法:从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的,那么虚拟机就判断是可回收对象。
-- 我开始看到这个GC Roots的时候没太搞懂这个是个什么东西?
-- 课程中比较官方的解释是:
GC Roots的对象有:
1、虚拟机栈(栈顿中的本地变量表)中引用的对象
在 Java 中,虚拟机栈中的本地变量表中引用的对象通常是基本数据类型的变量和对象的引用,而不是对象本身。例如,下面的代码创建了一个字符串对象并将其赋值给变量在这个过程
String str = "Hello World";
中,创建的字符串对象实际上是存储在堆内存中的,而在虚拟机栈中,只是存储了一个指向该对象的引用(即变量`str`的值),而不是实际的对象本身。该引用被存储在虚拟机栈的本地变量表中。又如,下面的代码创建了一个数组对象并将其赋值给变量`arr`:
int[] arr = new int[3];
同样地,创建的数组对象实际上是存储在堆内存中的,而在虚拟机栈中,只是存储了一个指向该对象的引用(即变量`arr`的值),而不是实际的对象本身。该引用被存储在虚拟机栈的本地变量表中。
其实,就是说我新建的任何一个对象,都可以是GC Roots;如
User user = new User();
String str=new String();
Integer i = 1;
// 等等
2、方法区中类静态属性引用的对象
在 Java 中,方法区中的类静态属性引用的对象通常是指类静态变量所引用的对象,这些静态变量只有一个副本,被所有类实例所共享。这些静态变量在类加载时被初始化,并存储在方法区中。
例如,下面的代码创建了一个Person
类,其中有一个类静态变量totalCount
表示创建的Person
对象的总数:
public class Person {
private static int totalCount = 0;
private String name;
public Person(String name) {
this.name = name;
totalCount++;
}
public static int getTotalCount() {
return totalCount;
}
}
在这个例子中,类静态变量totalCount
引用的对象是Person
类创建的所有对象的总数。在每次创建Person
对象时,totalCount
会自增1,该静态变量只有一个副本,被所有的Person
对象所共享。因此,无论创建多少个Person
对象,都只会有一个totalCount
静态变量,并引用一个整数对象表示当前Person
对象的总数。同时,静态变量totalCount
被存储在方法区中。
3、方法区中常量引用的对象
在 Java 中,方法区中常量引用的对象通常是指被 final 修饰的静态变量的值,也就是在编译期间确定的常量,被存储在常量池中,并在运行期间被直接引用。
例如,下面的代码创建了一个Constants
类,并在其中定义了一个常量PI
:
public class Constants {
public static final double PI = 3.1415926;
}
在这个例子中,常量PI
被存储在常量池中,并在运行期间被直接引用。因为PI
是被 final 修饰的,所以其值在编译期间就已经确定,即为3.1415926
。因此,在代码运行时,每次引用Constants.PI
时,都直接引用常量池中的3.1415926
,而不是创建一个新的对象并存储在方法区中。
另一个常用的常量是字符串常量,例如:
public class Constants {
public static final String GREETING = "Hello, World!";
}
在这个例子中,字符串常量"Hello, World!"
也被存储在常量池中,并在运行期间被直接引用。同样地,每次引用Constants.GREETING
时,都直接引用常量池中的字符串常量,而不是创建一个新的字符串对象并存储在方法区中。
4、本地方法栈中NI(即一般说的Native方法)引用的对象
在 Java 中,Native 方法是指在 Java 程序中调用本地(底层操作系统或其他系统)程序的方式。在本地方法调用时,本地方法栈中引用的对象可以是本地方法中所用到的参数或返回结果,这些对象通常是由本地方法所使用的第三方库或操作系统资源返回的。
例如,Java 中有一个`System`类,其中有一个 Native 方法`arraycopy()`,用于复制一个数组到另一个数组中。在本地方法`arraycopy()`中,参数传递的是数组对象的引用,这些对象通常被定义为在 Java 应用程序或库外部的资源。
以下是一个使用`System.arraycopy()`复制数组的例子:
public class ArrayCopyExample {
public static void main(String[] args) {
int[] source = {1, 2, 3, 4, 5};
int[] destination = new int[5];
System.arraycopy(source, 0, destination, 0, 5);
for (int i = 0; i < destination.length; i++) {
System.out.print(destination[i] + " ");
}
}
}
在这个例子中,`System.arraycopy()`通过本地方法调用将`source`数组复制到`destination`数组中。在 Native 方法`arraycopy()`中,`source`和`destination`都是数组对象的引用,这些对象通常是由 Java 应用程序或库外部的资源返回的。本地方法栈中引用的对象也可能是其他第三方库或操作系统资源返回的对象,例如文件句柄、网络连接句柄等等
总结一下:
1、所有new 出来的对象都是一个GC Roots 如果这些对象,没有被用到了,那么就可以被回收
2、方法内部的的静态变量(private和static同时修饰的一个变量)也是一个GC Roots.
3、方法内使用的常量(public、static、final)
4、调用系统的方法或者一些三方方法,传入和返回的对象
我想了一下,总结前三点来讲,如果是个对象,那么就是一个GCRoots.如果这个对象没有引用链了,那就可以被回收.第四个,我觉得也是,但是不确定.哈哈哈
可达性算法中的不可达对象并不是立即死亡的,对象拥有一次自我拯救的机会。对象被系统宣告死亡至少要经历两次标记过程:第一次是经过可达性分析发现没有与GC Roots相连接的引用链,第二次是在由虚拟机自动建立的Finalizerl队列中判断是否需要执行finalize()方法。
当对象变成(GC Roots)不可达时,GC会判断该对象是否覆盖了finalize方法,若未覆盖,则直接将其回收。否则,若对象未执行过finalize方法,将其放入F-Queue队列,由一低优先级线程执行该队列中对象的finalize方法。执行finalize方法完毕后,GC会再次判断该对象是否可达,若不可达,则进行回收,否则,对象“复活“每个对象只能触发一次finalize()方法
由于finalize()方法运行代价高昂,不确定性大,无法保证各个对象的调用顺序,不推荐大家使用,建议遗忘它。
这里还有个问题,因为我不是科班出生所以:
句柄(Handle)通常是指一种指向内存或资源的引用,它是一种非常常见的编程概念。句柄可以用来表示操作系统或程序中的各种对象,例如打开的文件、窗口、图形设备、网络连接等等。通过使用句柄,程序可以管理和操作这些对象。
我的理解就是一个操作指令的意思吧
以上内容,一部分是我学的课程中的内容,一部分是我自己查的以及自己的理解,还有一部分是gpt回答的.有不对的地方,还请各位雅涵,并留言,我也好知道哪里有问题.🙏😂