JVM性能篇

2020-02-05  本文已影响0人  依弗布德甘

类加载机制

class加载

类生命周期

从加载到卸载
  1. 加载 读取二进制内容
  2. 验证 验证class文件格式规范、语义分析、引用验证、字节码验证
  3. 准备 分配内存、设置类static修饰的变了初始值
  4. 解析 类、接口、字段、类方法等解析
  5. 初始化 为静态变了赋值、执行静态代码块
  6. 使用 创建实例对象
  7. 卸载 从JVM方法区中卸载
类生命周期

类加载器

一个java程序运行,最少有三个类加载器实例,负责不同的加加载

类加载器
  1. Bootstrap Loader 核心类库加载器
    • C/C++实现,无对应java类:null
    • 加载JRE_HOME/jre/lib目录,或用户配置的目录
    • JDK核心类库 rt.jar ... String..
  2. Extension Class Loader 拓展类库加载器
    • 加载JRE_HOME/jre/lib/ext目录,JDK拓展包,或用户配置的目录
  3. Application Class Loader 用户应用程序加载器
    • 加载java.class.path指定的目录,用户应用程序class-path 或者java命令运行时参数 -cp..

相关问题引导

查看类对应的加载器

/**
 * 查看类的加载器实例
 */
public class ClassLoaderView {
    public static void main(String[] args) throws Exception {
        // 加载核心类库的 BootStrap ClassLoader
        System.out.println("核心类库加载器:"
                + ClassLoaderView.class.getClassLoader().loadClass("java.lang.String").getClassLoader());
        // 加载拓展库的 Extension ClassLoader
        System.out.println("拓展类库加载器:" + ClassLoaderView.class.getClassLoader()
                .loadClass("com.sun.nio.zipfs.ZipCoder").getClassLoader());
        // 加载应用程序的
        System.out.println("应用程序库加载器:" + ClassLoaderView.class.getClassLoader());

        // 双亲委派模型 Parents Delegation Model
        System.out.println("应用程序库加载器的父类:" + ClassLoaderView.class.getClassLoader().getParent());
        System.out.println(
                "应用程序库加载器的父类的父类:" + ClassLoaderView.class.getClassLoader().getParent().getParent());
    }
}

JVM如何指定我们的类在何方

jvm 命令 查看类加载信息由上到下
jcmd :查看有哪些进程
jcmd -help : 帮助
jcmd pid (pid 是进程ID) help : 查看找执行当前进程的命令
jcmd pid VM.system_properties :查看属性配置

类不会重复加载

import java.net.URL;
import java.net.URLClassLoader;

/**
 * 指定class 进行加载e
 */
public class LoaderTest {
    public static void main(String[] args) throws Exception {
        URL classUrl = new URL("file:D:\\");//jvm 类放在位置

        // URLClassLoader loader  = new URLClassLoader(new URL[]{classUrl});

        while (true) {
            // 创建一个新的类加载器
            URLClassLoader loader = new URLClassLoader(new URL[]{classUrl});

            // 问题:静态块触发
            Class clazz = loader.loadClass("HelloService");
            System.out.println("HelloService所使用的类加载器:" + clazz.getClassLoader());

            Object newInstance = clazz.newInstance();
            Object value = clazz.getMethod("test").invoke(newInstance);
            System.out.println("调用getValue获得的返回值为:" + value);

            Thread.sleep(3000L); // 1秒执行一次
            System.out.println();

            //  help gc  -verbose:class
            newInstance = null;
            loader = null;
        }
        // System.gc();
    }
}

类的卸载

上述代码中 System.gc(); 为卸载类操作;loader需要放置在循环内部才会触发卸载

双亲委派模型

为了避免重复加载,由下到上逐级委托,由上到下逐级查找

  1. 首先不会自己去尝试加载类,而是把这个请求委派给父加载器去完成;
  2. 每一层次的加载器都是如此,因此所有的类加载请求都会传给上层的启动类加载器
  3. 只有当父加载器反馈自己无法完成该加载请求时,子加载器才会尝试自己去加载
import java.net.URL;
import java.net.URLClassLoader;

/**
 * 双亲委派机制
 */
public class LoaderTest1 {
    public static void main(String[] args) throws Exception {
        URL classUrl = new URL("file:D:\\");
        // 测试双亲委派机制
        // 如果使用此加载器作为父加载器,则下面的热更新会失效,因为双亲委派机制,HelloService实际上是被这个类加载器加载的;
        URLClassLoader parentLoader = new URLClassLoader(new URL[]{classUrl});

        while (true) {
            // 创建一个新的类加载器,它的父加载器为上面的parentLoader
            URLClassLoader loader = new URLClassLoader(new URL[]{classUrl}, parentLoader);

            Class clazz = loader.loadClass("HelloService");
            System.out.println("HelloService所使用的类加载器:" + clazz.getClassLoader());
            Object newInstance = clazz.newInstance();
            Object value = clazz.getMethod("test").invoke(newInstance);
            System.out.println("调用getValue获得的返回值为:" + value);

            // help gc
            newInstance = null;
            value = null;

            System.gc();
            loader.close();

            Thread.sleep(3000L); // 1秒执行一次
            System.out.println();
        }
    }
}

垃圾回收机制

内存回收-标记

内存回收第一步需要标记,标记哪些是需要被回收的内存

不同类型内存的判断机制

标价需要回收的内存

可达性分析算法
将对象及其引用关系看做一个图,选定活动的对象作为GC Roots
每个GC Root 都会去检测内存中是否还存在引用

可达性分析算法

引用行和可达性级别

引用类型

  1. 强引用(StrongReference):最长久的普通对象引用,只要还有强引用指向一个对象,就不会回收
  2. 软引用(SoftReference):JVM认为内存不足时,才会去试图回收软引用指向的对象。(缓存场景)
  3. 弱引用(WeakRefrence):虽然是引用,但随时可能被回收
  4. 虚引用(PhantomReference):不能通过它访问对象。供了对象呗finalize以后,执行指定逻辑的机制(Clenaner)

可达性级别

  1. 强可达(Strongly Reachable):一个对象可以有一个或多个线程可以不通过各种引用访问到的情况
  2. 软可达(Softly Reachable):就是当我们只能通过软引用才能访问到的对象状态
  3. 弱可达(Weakly Reachable):只能通过弱引用访问时的状态。当弱引用被清除的时候,就符合销毁条件
  4. 幻想可达(Phantom Reachable):不存在其他引用,并且finalize过了,只有幻想引用指向这个对象
  5. 不可达(unreachable):意味着对象可以被清除了

垃圾收集算法


分代收集

根据对象存活周期,将内存划分为几个区域,不同区域采用适合的垃圾收集算法

新的对象会分配到Eden,如果超过-XX:PretenureSizeThreshold:设置大对象直接进入老年代的阀值

垃圾收集器

串行收集 并发收集 G1

垃圾收集器组合


JDK内置命令工具

javap jstat jcmd jinfo jmap

Jconsole 工具

JDK自带工具,在java JDK bin 目录下

jconsole

Jvisualvm 工具

JDK自带工具,在java JDK bin 目录下,相比Jconsole更加灵活,可安装插件

Jvisualvm Jvisualvm-GC
上一篇 下一篇

猜你喜欢

热点阅读