• Java虚拟机(一)类加载、内存区域、垃圾回收策略


    JVM简介

    虚拟机是一种抽象化的计算机,Java虚拟机屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。

    字节码文件

    字节码文件也就是经过编译之后的class文件。

    执行流程简介

    执行一个class文件,JVM中的类装载子系统首先会将class文件装载进入的内存区域(运行时数据区),再由执行引擎执行程序。

    类加载机制

    指的是将类的class文件的二进制数据读入到内存中,并将其放入到运行时数据区中的方法区内,并在堆区中创建一个对应的class对象。

    类加载过程

    1. 加载:通过IO读入字节码文件;
    2. 连接:执行校验、准备、解析的步骤;
    3. 初始化:对类的静态变量进行初始化成指定的值,以及执行静态代码块;
    4. 使用;
    5. 卸载

    类的初始化和卸载

    初始化

    类的初始化,包括生成对象的初始化和类的静态块的实例化。

    初始化触发的时机: 类被直接引用(主动引用)的时候。

    主动引用

    主动引用的情形有:

    1. 使用new关健字实例化对象

    2. 使用类的静态变量

    3. 使用类的静态方法

    4. 使用反射机制调用上述操作

    5. 程序入口 (调用main方法)

    初始化顺序是:

    静态块 -> 非静态块 -> 构造函数。

    如果有父类,则初始化顺序是:
    父类静态块 -> 子类静态块 -> 父类非静态块 -> 父类构造函数 -> 子类非静态块 -> 子类构造函数

    被动引用

    被动引用不会触发类的初始化。

    被动引用的情形:

    1. 引用父类的静态字段,只会初始化父类,不会初始化子类。
    2. 引用类的常量,不会引起类的初始化。

    卸载

    在类使用完之后,满足下面的情形,会被卸载:

    • 该类在堆中的所有实例都已被回收,即在堆中不存在该类的实例对象。
    • 加载该类的classLoader已经被回收。
    • 该类对应的Class对象没有任何地方可以被引用,通过反射访问不到该Class对象。
      如果类满足卸载条件,JVM就在GC的时候,对类进行卸载,即在方法区清除类的信息。

    类加载器的种类

    • 启动类加载器:加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如rt.jar、charsetsjar等;
    • 扩展类加载器:加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的JAR类包;
    • 应用程序类加载器:加载ClassPath路径下的类包,主要就是加载自己写的那些类;
      自定义加载器:加载用户自定义路径下的类包。

    运行时内存区域

    分为两大类:线程私有区域、线程共有区域

    线程私有区域:

    • 程序计数器:吉利字节码执行的行号;
    • java虚拟机栈:执行方法的时候,方法形成栈帧进行压栈,方法执行完毕时,栈帧出栈;
      栈帧:包含局部变量表、操作数栈、方法出口等;
    • 本地方法栈:调用native方法时产生的栈。

    线程共有区域:

    • 堆内存: 分配所有对象实例的地方,也是垃圾回收的主要区域,也是内存分配最大的区域;
    • 元数据区(方法区):虚拟机啊加载的类信息,常量,静态变量;
    • 直接内存:非虚拟机内存,物理机的直接内存。

    内存分配和垃圾回收策略

    堆内存划分为年轻代,老年代。年轻代划分为eden区和survivor区,survivor区分为from区和to区;

    对象优先在Eden区分配

    大多数情况下,对象在eden区分配,eden区没有足够内存时,触发minorGC/YoungGC;

    大对象直接进入老年代

    可通过配置-XX:PretenureSizeThreshold来设置直接进入老年代的阈值,这样可以避免大对象在Eden区创建,因为大对象比较大,会造成minorGC的效率下降。

    长期存活的对象将进入老年代

    虚拟机给每个对象一个年龄计数器,Eden区触发minorGC,存活对象复制到from区,此时对象年龄设为1,之后的每一次熬过一次minorGC,年龄都会加1,当年龄增加到一定程度(默认15,可通过XX:MaxTenuringThreshold=数字 参数可以设置对象在经过多少次minorGC后会被放入老年代)

    minorGC后存活的对象Survivor区放不下

    这种情况时,JVM会将部分对象放入老年代,部分对象放入survivor区

    Eden区和Survivor区内存分配比例

    默认8:1:1

    对象动态年龄判断

    survivor区的一批对象的总大小大于这块survivor区总大小的50%,大于等于这批对象中最大年龄的对象的对象,就能直接进入老年代。
    这篇博客写到挺详细:
    https://www.jianshu.com/p/989d3b06a49d

    老年代空间分配担保机制

    JVM如何判断对象是否存活

    1. 引用计数法: 当对象引用计数器为0时,即无其他对象引用,判定为死亡,可被回收,但是存在循环引用问题,导致对象一直存活
    2. 可达性分析法:从GC root根向下搜索直接或间接可达的对象,判定为存活
  • 相关阅读:
    关于GitHub推送时发生Permission denied (publickey)的问题
    线性模型——机器学习(西瓜书)读书笔记
    梯度下降算法的简单理解
    PRML学习笔记第一章
    python函数学习之装饰器
    机器学习 概论
    Mybatis
    Nginx 常用配置清单
    接口,抽象类
    IntelliJ IDEA打war包
  • 原文地址:https://www.cnblogs.com/qiutianyou/p/15350564.html
Copyright © 2020-2023  润新知