JVM虚拟机

目录

一.JVM概述

1.jvm的作用?

2.jvm内部构造?

二.类加载器

1.类加载器子系统概述

2.类加载过程

1.加载

2.链接

1.验证

2.准备

 3.解析

3.初始化

3.类在哪种情况下会被定义?

4.类在那两种情况下不会被加载?

5.类加载器的分类

6.双亲委派机制

三.运行时数据区

1.程序计数器

2.虚拟机栈

3.本地方法栈

4.堆

5.方法区

四.本地方法接口

1.什么是本地方法?

 2.为什么要使用本地方法?

五.执行引擎

1.作用

2.什么是解释器?什么是 JIT编译器?

3.为什么java是半编译半解释的语言?

4.JIT编译器效率高为什么还要使用解释器?

六.垃圾回收

1.垃圾回收概述

a.什么是垃圾?

b.早期垃圾回收

c.哪些区域可以回收垃圾?

d.内存溢出与内存泄漏

2.垃圾回收相关算法

1.标记阶段算法

a.引用计数算法(目前没有被使用)

b.可达性分析算法

c.哪些对象可以被认定为是活跃对象?

d.finalize()

2.回收阶段算法

a.标记-复制算法

b.标记-清除算法

c.标记-压缩算法

分代收集

3.垃圾回收器


一.JVM概述

1.jvm的作用?

    将字节码文件装载到虚拟机中并将java字节码文件转为机器码文件。

    当前的jvm不仅可以将java字节码转为机器码,同样也可以执行其他语言编译后的字节码文件。

2.jvm内部构造?

    1.类加载器:负责将硬盘上的字节码文件加载到内存当中(运行时数据区)

    2.运行时数据区:负责存储运行时产生的各种数据 类的信息,静态变量,方法信息.....

    3.执行引擎:负责将.class文件转为机器码文件

    4.本地方法接口:调用本地方法 native修饰的

    5.垃圾回收部分:负责对运行时数据区中的堆和方法区的空间进行垃圾回收

        程序在执行之前先要把 java 代码转换成字节码(class 文件),jvm 首先需要把字节码通过一

定的方式 类加载器(ClassLoader) 把文件加载到内存中的运行时数据区(Runtime Data Area)

,而字节码文件是 jvm 的一套指令集规范,并不能直接交个底层操作系统去执行,因此需要特定的

命令解析器 执行引擎(Execution Engine) 将字节码翻译成底层系统指令再交由CPU 去执行,而

这个过程中需要调用其他语言的接口 本地库接口(NativeInterface) 来实现整个程序的功能,这

就是这 4 个主要组成部分的职责与功能。

二.类加载器

1.类加载器子系统概述

        类加载器子系统负责从文件系统或者网络中加载 class 文件。类加载系统只负责 class 文件的加载,至于它是否可以运行,则由执行引擎决定。加载的类信息存放于一块称为方法区的内存空间。

        类加载系统,负责将硬盘上的字节码文件加载到jvm中,生成类的Class对象,存储在方法区。类就是一个模板。

2.类加载过程

1.加载

         以二进制文件流进行读取文件,在内存中为类生成Class对象。

2.链接

1.验证

        验证字节码文件结构是否正确,以 CA FE BA BE 标识开头)

2.准备

        为类的静态属性进行分配内存空间,并进行设置默认初始值。

public static int value = 123;

        value 在准备阶段后的初始值是 0,而不是 123.

 3.解析

        将字节码的符号引用 替换为 内存中的直接引用地址。

3.初始化

对准备阶段分配的静态属性进行正确的初始化。

3.类在哪种情况下会被定义?

             new 类的对象

            调用类中的静态成员(方法,变量)

            在类中执行main()方法

            反射加载类 Class Class.forName("地址")

            子类被加载时,父类也会被一块加载。

4.类在那两种情况下不会被加载?

①当调用类中的静态常量时类就不会被加载。

static final 修饰的

public final static int NUMBER = 5 ; //不会导致类初始化,被动使用

public final static int RANDOM = new Random().nextInt() ; //会导致类加载

②new一个对象数组时类不会被加载。

 Demo[] d=new Demo[10];//注意现在new的是数组对象,而不是Demo对象,Demo只是数组类型而已。

注意现在new的是数组对象,而不是Demo对象,Demo只是数组类型而已。

5.类加载器的分类

站在jvm的角度上分为:

1.引导类加载器(不是由java代码写的,是由c/c++写的),负责读取加载java中的底层系统库。

2.java写的类加载器(用来读取我们写的java应用程序)。

再细分类加载器:

    1.启动类加载器

            C/C++语言实现,负责加载java核心类库(系统库 .lang等)

    2.扩展类类加载器

            用java语言实现的,继承于ClassLoader类,从 java.ext.dirs 系统属性所指定的目录中加载类库,或从 JDK 系统安装目录的

            jre/lib/ext 子目录(扩展目录)下加载类库.如果用户创建的 jar 放在此目录下,也会自动由扩展类加载器加载.

    3.应用程序类加载器

            java语言实现的,继承于ClassLoader类,加载我们自定义的类,用于加载用户类路径(classpath)上所有的类。该类加载器是程序中默认的类加载器.

ClassLoader 类 , 它是一个抽象类,其后所有的类加载器都继承自ClassLoader(不包括启动类加载器)。

6.双亲委派机制

        jvm对class文件采用的是按需加载的方式,也就是需要用到某个类时,jvm才会将class文件加载到内存中生成class对象。而且在加载class类对象时,jvm采用的是双亲委派模式,即把请求交给父类处理,它是一种委派机制。

工作原理:

    1.当一个类加载器收到了加载请求时,他先不会自己去加载,而是看自己有没有父类加载器能加载,可以的话就先交给父类加载器去加载。

    2.如果父类加载器向上还有父类加载器,那就继续往上委托,依次递归,直到找到顶层的加载器为止。

    3.如果顶层父类加载器不能够加载请求类时,就向下由子类加载器自己加载。

为什么使用双亲委派机制?

①优先加载系统中的类,防止自定义类将系统中的类替换。

②如果到了最底层都没有加载的话,那么就只能抛出ClassNotFoundException异常。

双亲委派机制的优点

  安全,当自定义类存在和java类库中同样的类包和类名时,可避免用户自己编写的类替换 Java 的核心类,如 java.lang.String.

如何打破双亲委派机制?

    自定义类加载器

    Java 虚拟机的类加载器本身可以满足加载的要求,但是也允许开发者自定义类加载器。

    在 ClassLoader 类中涉及类加载的方法有两个,loadClass(String name),

     findClass(String name),这两个方法并没有被 final 修饰,也就表示其他子类可以 重写.

    重写 findClass 方法

    我们可以通过自定义类加载重写方法打破双亲委派机制,

    再例如 tomcat 等都有自己定义的类加载器.

三.运行时数据区

功能:存储运行时的各种数据

1.程序计数器

用于记录在某一个线程中执行到的位置。

特点:

      内存小,速度最快。

      是每一个线程私有的.

      不存在内存溢出,不存在垃圾回收

2.虚拟机栈

用于解决线程中的方法。

特点:

        栈 先进后出/后进先出.

        是每一个线程私有的.

         存在内存溢出:栈压爆了(递归)

         不存在垃圾回收.

         每个虚拟机栈都是相互隔离的,互不打扰。

每一个方法都是一个栈帧。

栈帧的内部结构:

1.局部变量表(存储方法中生成的局部变量)

2.操作数栈(记录操作/过程的)

3.方法返回地址(就是一个方法中调用另一个方法,另一个方法执行完之后回到该方法的位置继续向下执行)

3.本地方法栈

本地方法栈中放的是本地方法。

特点:

        栈先进后出。

        存在内存溢出。

        没有垃圾回收。

        是每个线程私有的。

4.堆

堆用来存放生成的对象。

特点:

        堆空间对于每个线程来说是共享的。

        堆中是有垃圾回收的。

        堆也会存现内存溢出问题。

        堆是jvm中区域最大的一部分,并且jvm中除了程序计数器,其余四个区域的空间都是可以自定义设置的。

堆的分区?

新生代/新生区

        伊甸园区  幸存者0(from)  幸存者1(to)

老年代/老年区

为什么要分区?

可以把存活时间不同的对象分到不同的区,频繁回收新生代,减少回收老年代。

对象在堆中的分配过程

①新new出来的对象都是先存在于伊甸园区。

②经过一次垃圾回收之后,伊甸园区中存活下来的对象被存放到幸存者0中。

③第二次垃圾回收时,伊甸园区中存活下来的对象和幸存者0中存活的对象被存放到幸存者0中。

④循环往复,直到某一个对象已经经历过15次垃圾回收时,就会将该对象存放到老年区中。

⑤因为对象头中有字段存储的就是垃圾回收的次数,长度为4bit,也就是1111=>15。

⑥幸存者0 和幸存者1 中总会存在一个区域是空闲的。

存入老年区的两个案例:

①经历15次垃圾回收

②内存比较大(eg对象数组)直接存到老年区。

堆空间的参数配置:

-Xmx:最大堆空间内存

-Xmn:设置新生代的大小

-Xms:初始堆空间内存

-XX:MaxTenuringTreshold:设置新生代垃圾的最大年龄

-XX:+PrintGCDetails 输出详细的 GC 处理日志

5.方法区

方法区用来存储加载类的信息(类字节码,静态常量,静态变量等等)。

特点:

        方法区物理上和堆是一个空间。

        方法区是所有线程共享的。

        方法区存在垃圾回收和内存溢出。

        方法区的大小也是可以设置的。

        元数据区大小可以使用参数-XX:MetaspaceSize 指定。方法区一旦装满就会发生 Fall GC (整堆收集)

方法区的垃圾回收也称为类卸载。

判定一个类不会再使用的三个条件?

①该类的所有对象实例不会再使用

②该类的类加载器已经被回收

③该类的Class对象已经被回收

四.本地方法接口

1.什么是本地方法?

就是给操作系统所调用的,java不能调用,由C/C++实现。

native修饰的

eg:

    Object.class.hasCode()  public native int hashCode();//哈希
    Thread.start()  private native void start0();  //线程启动
    new FileInputStream("").read();  private native int read0()// 读硬盘数据

 2.为什么要使用本地方法?

与硬件进行交互,因为java语言是应用程序语言,没有权限直接操作硬件。

例如获取内存地址,读取硬盘数据这时就需要调用操作系统中的本地方法进行实现.

五.执行引擎

1.作用

将字节码文件解释/编译为机器码

两个编译:

    前端编译:将.java文件编译为.class文件

    后端编译:将字节码文件编译为机器码

2.什么是解释器?什么是 JIT编译器?

解释器:对字节码采用逐行解释的方法,立即执行,但是执行效率低。

JIT编译器:先将字节码进行编译操作,所以不是立即执行,所以开始慢需要花费时间编译,但是编译完之后执行效率就很高了。

3.为什么java是半编译半解释的语言?

     起初将 Java 语言定位为“解释执行”还是比较准确的。再后来,Java 也发展出可以直接生成本地代码的编译器。现在 JVM 在执行 Java 代码的时候,通常都会将解释执行与编译执行二者结合起来进行。

4.JIT编译器效率高为什么还要使用解释器?

 ①程序启动后,解释器可以立即执行,响应速度快,省去编译的时间。

②而编译器需要先进行编译操作,需要消耗时间,但是编译为本地代码之后执行效率就很高了。

所以需要采用解释器+编译器并存的架构来换取一个平衡点。

六.垃圾回收

1.垃圾回收概述

a.什么是垃圾?

在运行的程序中,没有任何引用所指向的对象就称为垃圾。

b.早期垃圾回收

早期垃圾回收都是用C/C++实现,并且在new对象时需要先申请空间,并且最后在不使用这个对象时需要手动地将垃圾空间释放。

优点:可以使程序员自行的管理内存空间

缺点:比较麻烦,每次都需要去空间申请和释放,如果未及时的释放垃圾,则可能出现内存泄漏。

现在的垃圾回收(eg:Java,Python,C#....)都是自动的,不需要申请空间和手动释放内存空间。

优点:简化代码,避免了繁忙的内存申请和释放。

缺点:弱化了程序员的能力。

c.哪些区域可以回收垃圾?

在运行时数据区的堆和方法区中存在垃圾回收。

堆空间为主要垃圾回收部分,方法区中的垃圾回收一般都是 full gc(整堆收集)。

堆:频繁回收新生代,较少回收老年代。

d.内存溢出与内存泄漏

内存泄漏:

        指在垃圾回收区域中,未能及时回收垃圾的情况。

内存溢出:

        应用系统中无法回收的内存或者使用的内存过多,最终使得程序需要执行的内存空间不够用,程序运行不了,就会提示内存溢出。

2.垃圾回收相关算法

1.标记阶段算法

标记阶段一般是用来标记堆中的哪些对象需要回收,哪些是垃圾对象,把没有引用的指向的对象标记出来。

在垃圾回收时进行垃圾回收.

a.引用计数算法(目前没有被使用)

当一个对象被引用时,其中的引用计数就会+1,最终系统会自动清除计数值为0的垃圾对象。

缺点:

当存在循环利用时,,无法解决循环利用的问题。

在循环体中,对象之间只是相互引用,对于外界来说没有用,所以应当被判定为垃圾对象,但是由于之间存在相互引用,所以计数值并不为0,所以在这种算法下不会被判定为垃圾对象,实际上是应该被回收的。

循环引用未被回收还可能存在垃圾泄露的问题。

b.可达性分析算法

这种算法可以解决循环引用的问题。

思想:从一些活跃的对象开始查找,与活跃对象相连的对象就不是垃圾对象,没有与活跃对象相连的就是垃圾对象。

c.哪些对象可以被认定为是活跃对象?

①虚拟机栈中引用的对象

②方法区中静态属性引用的对象

③所有同步锁(synchronized)引用的对象

④Java虚拟机内部调用的对象

d.finalize()

finalized()是Object类中的一个受保护的方法。

当一个对象被判定为垃圾对象,在垃圾回收这个垃圾对象之前就会调用finalized()方法。

finalized()方法规定只能被调用一次,当一个对象被垃圾回收是调用了finalized()方法之后,又因为方法体中的语句使得对象复活,那么这个对象再次被当作垃圾对象回收时就不会再调用这个finalized()方法了。

我们可以对finalized()方法进行重写,可以使垃圾对象在被回收之前执行一些操作,但是切记我们不要手动的去调用finalized()方法,还有此方法中的内容也要注意,不然会影响垃圾回收的性能。

由于finalized()存在,我们把对象分为三类:

        可触及:使用中的对象,没有被判定为是垃圾对象。

        可复活:垃圾对象,还没有调用finalized()方法,可能会在方法中复活。

        不可触及:第二次被判定为垃圾对象,已经调用过finalized()方法。

2.回收阶段算法

a.标记-复制算法

将一个空间中存活下来的对象复制放到另一个空间中,位置不改变,并且清空上个内存空间中的对象。

特点:存在多个内存空间

           回收时需要移动对象,适用于存活对象少,内存小,垃圾多的场景(适用于新生代)

           回收后,垃圾碎片多。

b.标记-清除算法

只在一个内存空间中,清除所有的垃圾对象,但也不是真正的清除,是将垃圾对象的地址存放到空闲的地址列表当中,新对象来的时候能放进垃圾空间就直接覆盖。

特点:只存在一个空间

           回收不需要移动对象,只清除垃圾对象

           回收后,存在垃圾碎片

           适用于老年代(不移动对象)。

c.标记-压缩算法

只存在一个内存空间,就是在标记-清除算法的基础上,将存活下来的对象移动到内存空间的一侧,清除其余空间。

特点:只存在一个空间,

           是一种移动式的垃圾回收算法,内存中就不存在碎片化问题

           适用于老年代回收情况。

分代收集

新生代的垃圾回收一般采用标记-复制算法,标记-清除算法,垃圾多存活对象少,进行多次回收。

老年代的垃圾回收一般采用标记-压缩算法,垃圾少,存活对象多,进行少次回收。

3.垃圾回收器

垃圾回收器是jvm垃圾回收的的实现者。

垃圾回收器的种类很多,不同的虚拟机中可以使用不同的垃圾回收器。

垃圾回收器的分类:

  从垃圾收集器线程数量上划分:

        单线程:垃圾回收器中只能有一个线程进行垃圾回收操作。

        多线程:垃圾回收器中有多个线程在执行垃圾回收操作。

  从工作模式上划分:

        独占式:当垃圾回收线程正在执行,其他线程就会暂停(stw)。

        并发式:当垃圾回收线程正在执行,其他线程也是可以执行的。

  按照工作内存划分:

            新生代垃圾收集器

            老年代垃圾收集器

CMS(Concurrent Mark Sweep,并发标记清除)收集器

首创了垃圾回收线程和用户线程并发执行,追求低延时。

垃圾回收过程:

    初始标记阶段:独占式的

    并发标记阶段:并发式的

    重新标记阶段:独占式的

    并发清除阶段:并发式的

G1(Garbage First)垃圾收集器

首先G1垃圾收集器适用于整堆收集,不再划分新生代和老年代。

适用于服务器场景.

G1垃圾收集器将每个区域划分为更小的区域,例如将伊甸园区划分为多个小区域,

优先回收垃圾对象较多的区域,故得名Garbage-First----垃圾优先。

也是支持并发执行的.

查看并设置垃圾收集器

打印默认垃圾回收器
-XX:+PrintCommandLineFlags -version    打印jdk中使用的垃圾收集器信息.
打印垃圾回收详细信息
-XX:+PrintGCDetails -version
-XX:+UseG1GC 设置垃圾收集器版本
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值