zoukankan      html  css  js  c++  java
  • 深入理解java虚拟机


    title: 深入理解Java虚拟机
    date: 2020-05-14 10:58:24
    tags: JVM,虚拟机

    1.运行时数据区域

    1.程序计数器

    当前线程执行字节码的行号指示器
    记录正在执行的虚拟机字节码指令的地址(如果正在执行的是本地方法,则为空)

    2.虚拟机栈

    虚拟机栈描述的是java方法执行的内存模型:方法在执行的时候创建一个栈帧(Frame)
    栈帧中存储着(局部变量,操作数栈,常量池引用)

    3.本地方法栈

    为虚拟机使用Native方法服务

    4.Java堆

    所有对象都在堆上分配,是垃圾收集的主要区域

    主要的垃圾回收算法都是分代收集算法,针对不同类型的对象采取不同的垃圾回收算法

    堆:

    • 新生代(Young Generation)
    • 老生代(Old Generation)

    4.方法区

    方法区的别名叫非堆(No-Heap),也可以称之为永久代

    用于存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,垃圾回收将其作为永久代进行回收

    运行时常量池

    运行时常量池是方法区的一部分,存放编译期生成的各种字面量和符号引用

    从 JDK 1.8 开始,移除永久代,并把方法区移至元空间,它位于本地内存中,而不是虚拟机内存中

    原来永久代的数据被分到了堆和元空间中。元空间存储类的元信息,静态变量和常量池等放入堆中。

    2.GC垃圾回收

    内存回收三问?

    那些内存需要回收?

    什么时候进行回收?

    如何回收?

    1.如何判断一个对象是否可以进行回收

    引用计数法

    可达性分析算法

    2.垃圾收集算法

    标记-清除

    效率问题:标记和清除的效率都不太高
    空间问题:清除后的内存空间不连续
    

    复制

    主要是解决标记-清除算法的效率问题

    将内存划分为较大的Eden和Survivor空间 8:1

    用于新生代的回收

    标记-整理

    解决空间问题

    将存活的对象移动到一侧

    用于老年代的回收

    
    垃圾收集采用分代收集算法,java堆分成老年代和新生代,根据特点采用不同的收集算法
    

    3.垃圾收集器

    Serial,ParNew,Parallel Scavenge,Serial Old,Parallel Old,CMS,G1收集器

    3.内存分配与回收策略

    内存分配策略

    1.对象优先Eden分配

    Eden空间不够会发生一次Minor GC

    2.大对象直接进入老年代

    大对象需要连续的内存空间,避免在新生代Eden和Survivor之间大量内存复制

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

    制定年纪计数器,增加到一定年龄放入到老年代中
    -XX:MaxTenuringThreshold ,用来定义年龄的阈值

    4.动态对象年龄判定

    并不是所有对象必须达到MaxTenuringThreshold才能晋升到老年代
    当相同年龄的所有对象的总和大于Survivor的一半,这些对象可以直接进行老年代

    5.空间分配担保

    使用老年代的空间来进行担保MinorGC

    老年代的最大可用的连续空间大于新生代的空间总和时,MinorGC是安全的

    内存回收策略

    先进行MinorGC,老年代的空间内存担保失败,则进行FullGC(MajorGC)

    FullGC的触发条件

    • 调用System.gc()
    • 老年代的空间不足
    • 空间分配内存担保失败

    4.类加载机制

    Class的类文件结构

    Class文件是以8位字节为基础的二进制流

    每个Class的头四个字节称为魔数:"0xCAFEBABE"

    虚拟机的类加载机制:

    虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型

    加载,连接,初始化和运行:
    虚拟机加载流程

    其中解析(Resolution)和初始化(Initialization)的顺序不一定,为了实现Java运行时绑定(动态绑定),可以在初始化阶段之后再开始

    1.加载

    这个加载只是类加载的一个过程,要注意区分

    加载过程中的主要事项:

    • 1.通过类全限定名来获取定义该类的二进制字节流

    • 2.将该字节流表示的静态存储结构转换为方法区的运行时存储结构

    • 3.从内存中生成一个代表该类的Class对象,作为方法区中该类各种数据的访问入口

    2.验证

    验证是连接阶段的第一步

    验证的目的是,确保Class文件的字节流中包含的信息符合当前虚拟机的要求

    是虚拟化保护自身安全一项重要工作

    • 文件格式验证
    • 元数据验证
    • 字节码验证
    • 符号引用验证

    3.准备

    准备阶段为类变量(static 修饰)分配内存并设置初始值,使用的是方法区的内存。

    public static int value=123;
    

    初始值一般是0,而不是123

    public static final int value=123;
    

    类变量是常量,那么它将初始化为表达式所定义的值而不是 0,上面value的初始值为123

    4.解析

    常量池的符号引用替换直接引用的过程

    • 类或接口的解析
    • 字段解析
    • 类方法解析
    • 接口方法解析

    5.初始化

    初始化阶段才真正开始执行类定义的java程序代码

    初始化阶段是虚拟机执行<clinit>()方法的过程,在准备阶段,类变量已经赋过一次系统要求的初始值,而在初始化阶段,根据程序员通过程序制定的主观计划去初始化 类变量和其它资源。

    父类的<clinit>()方法先执行,父类中定义的静态语句块的执行要优先于子类

    5.类与类加载器

    两个类相等=两个类本身相等+使用同一个类加载器进行加载(每个类加载器都有一个独立的类名称空间)

    类加载器的分类:

    从java虚拟机的角度:

    • 1.启动类加载器

      Bootstrap Classloader,使用C++实现,是虚拟机的一部分

    • 2.所有其他类的加载器,使用Java实现,独立于虚拟机,继承自java.lang.ClassLoader

    类加载器细分:

    • 1.启动类加载器
    • 2.拓展类加载器
    • 3.应用程序类加载器,如果没有自定义类加载器,这个就是程序的默认加载器
    • 4.自定义的类加载器

    双亲委派模型

    双亲委派模型就是类加载器的的层次关系

    • 工作过程:一个类加载器首先将类加载请求转发到父类加载器,只有父类加载器无法完成时才尝试自己加载
    • 好处:优先级的层次关系,基础类得到统一
  • 相关阅读:
    解决“google快照无法打开”的简单而有效的方法~
    在Struts2里面嵌入Spring
    HDU
    设计模式大总结(二)
    Node.js入门笔记
    草图检索和识别[开源]
    2019-10-31-VisualStudio-断点调试详解
    2019-10-31-VisualStudio-断点调试详解
    2019-9-2-C#-设计模式-责任链
    2019-9-2-C#-设计模式-责任链
  • 原文地址:https://www.cnblogs.com/GeekDanny/p/12919867.html
Copyright © 2011-2022 走看看