likes
comments
collection
share

JVM工作原理与实战(二十二):方法区的垃圾回收

作者站长头像
站长
· 阅读数 2

前言

JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了Java的内存管理和自动垃圾回收、方法区的垃圾回收等内容。


一、Java的内存管理和自动垃圾回收

运行时数据区知识回顾:

Java虚拟机(JVM)在运行Java程序期间,会创建并维护一系列内存区域,这些区域总称为运行时数据区。这些区域根据其用途和特性,被严格定义并管理。《Java虚拟机规范》详细规定了这些区域的作用和行为,以确保所有Java虚拟机实现的一致性和正确性。

线程不共享区域:

  • 程序计数器:用于存储当前线程执行的字节码指令地址。这个区域是每个线程独有的,不共享。
  • Java虚拟机栈:每个线程在创建时都会创建一个虚拟机栈,每个方法调用都会创建一个栈帧,用于存储局部变量、操作数栈、动态链接和方法出口信息。
  • 本地方法栈:与虚拟机栈相似,本地方法栈为native方法提供服务。

线程共享区域:

  • 方法区:用于存储已被JVM加载的类信息、常量、静态变量以及即时编译器编译后的代码等数据。
  • 堆:堆是所有线程共享的区域,用于动态分配内存。所有的对象实例以及数组都应当在堆上分配。

JVM工作原理与实战(二十二):方法区的垃圾回收

在运行时数据区中,线程不共享的部分都是伴随着线程的创建而创建,随着线程的销毁而销毁。而方法的栈帧在执行完方法之后就会自动弹出栈并释放掉对应的内存。这种内存管理方式可以有效地减少内存的浪费,并且可以防止内存泄漏问题的发生。

JVM工作原理与实战(二十二):方法区的垃圾回收

二、方法区的垃圾回收

1.回收条件

在Java中,方法区用于存储已被虚拟机加载的类信息、常量、静态变量以及即时编译器编译后的代码等数据。与堆区一样,方法区也有垃圾回收机制,主要是对不再使用的类进行回收。

要判定一个类是否可以被卸载,需要同时满足以下三个条件:

  • 此类所有的实例对象都已经被回收,即在堆区中不存在该类的任何实例对象和子类对象。
  • 加载该类的类加载器已经被回收。在Java中,类加载器通过ClassLoader类实现,当一个类加载器不再被引用,或者其父类加载器也被回收时,该类加载器会被回收。
  • 该类对应的java.lang.Class对象没有被其他对象引用。如果一个Class对象没有被其他任何对象引用,那么这个Class对象可以被回收。

2.手动触发垃圾回收

虽然垃圾回收是自动进行的,但在某些情况下,可能需要手动触发垃圾回收。这可以通过调用System.gc()方法实现。这个方法会向Java虚拟机发送一个请求,要求进行垃圾回收。

System.gc();

需要注意的是,调用System.gc()并不一定会立即执行垃圾回收。Java虚拟机可能会根据当前的运行情况自行判断是否需要进行垃圾回收。因此,手动触发垃圾回收并不一定能够提高程序的性能。

在开发中,手动触发垃圾回收的情况并不常见。但在一些特殊的应用场景中,如OSGi框架和JSP的热部署等,可能需要手动触发垃圾回收。在这些场景中,当一个jsp文件被修改后,对应的类加载器会被卸载,然后重新创建类加载器并重新加载jsp文件。

3.方法区的垃圾回收案例

案例:

此类所有的实例对象都已经被回收,即在堆区中不存在该类的任何实例对象和子类对象。

public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        try {
            URLClassLoader loader = new URLClassLoader(new URL[]{new URL("file:D:\Test\")});
            Class<?> clazz = loader.loadClass("com.rye.test.Test");
            Object o = clazz.newInstance();
            o = null;
            System.gc();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

JVM工作原理与实战(二十二):方法区的垃圾回收

案例:

加载该类的类加载器已经被回收。在Java中,类加载器通过ClassLoader类实现,当一个类加载器不再被引用,或者其父类加载器也被回收时,该类加载器会被回收。

public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        try {
            URLClassLoader loader = new URLClassLoader(new URL[]{new URL("file:D:\Test\")});
            Class<?> clazz = loader.loadClass("com.rye.test.Test");
            Object o = clazz.newInstance();
            loader = null;
            System.gc();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

JVM工作原理与实战(二十二):方法区的垃圾回收

案例:

该类对应的java.lang.Class对象没有被其他对象引用。如果一个Class对象没有被其他任何对象引用,那么这个Class对象可以被回收。

public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        try {
            URLClassLoader loader = new URLClassLoader(new URL[]{new URL("file:D:\Test\")});
            Class<?> clazz = loader.loadClass("com.rye.test.Test");
            Object o = clazz.newInstance();
            clazz = null;
            System.gc();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

JVM工作原理与实战(二十二):方法区的垃圾回收


总结

JVM是Java程序的运行环境,负责字节码解释、内存管理、安全保障、多线程支持、性能监控和跨平台运行。本文主要介绍了Java的内存管理和自动垃圾回收、方法区的垃圾回收等内容,希望对大家有所帮助。