JVM--java虚拟机

一、JVM概述:

为什么学习JVM:

  面试的需要,基础部分不能区分基础好坏

  对JAVA程序运行的过程更加了解

  为后期写出优质的代码做好准备

  项目管理、性能调优

虚拟机:

在Windows中,虚拟一个运行环境

系统虚拟机:VMware

       可运行完整操作系统的软件平台

程序虚拟机:JVM

         专门执行某个单个计算机指令

jvm作用:

负责将字节码装载到java内部(运行时效数据区)

负责存储数据

把字节码翻译为机器码,执行

垃圾回收

JVM特点:

一次编译到处运行

自动内存管理

自动垃圾回收功能

可执行多语言编译后的字节文件

jvm组成部分:

1.类加载器(负责加载字节码文件)

2.运行时数据区(存储运行时数据,堆,java虚拟机栈(运行java自己的方法),方法区,程序计数器,本地方法栈)

3.执行引擎(更底层,把字节码翻译成机器码)

4.本地方法接口

5.垃圾回收

二、JVM结构-类加载

2.1类加载子系统

作用:

负责从硬盘/网络中加载字节码信息

加载到内存中(运行时数据区的方法区中)

2.2类加载过程

2.2.1加载

使用IO读取字节码文件、

转换并存储,为每个类创建一个Class类的对象

存储在方法区中

2.2.2链接(验证,准备,解析)

验证:对字节码格式进行验证,文件是否被污染。

          对基本的语法格式进行验证。

准备:为静态的变量进行内存分配

public static int value = 123;value 在准备阶段后的初始值是 0,而不是 123

          不包含用final修饰的static常量,静态常量在编译期间进行初始化

2.2.2解析:

将类的二进制数据中的符号引用替换成直接引用。

将字节码中的表现形式,转化为内存中的表现(内存地址)

2.2.3初始化

类的初始化,为类中的定义的静态变量进行赋值。

 public static int value = 123;value 在初始化阶段后值是 123.

类什么时候会被加载(初始化)

1.在类中运行main方法

2.创建对象

3.使用类中的静态变量、静态方法

4.反射Class.forName("类的地址")

5.子类被加载

以下两种情况不会被初始化:

编译期间赋值的静态常量

作为数组

2.3类加载器分类

具体负责加载类的一些代码

2.3.1引导类加载器(启动类加载器)

用C/C++语言开发,jvm底层的开发语言,负责加载java核心类库,与java语言无关的。

2.3.2扩展类加载器

java 语言编写的,由 sun.misc.Launcher$ExtClassLoader 实现,继承ClassLoader类.

 从 JDK 系统安装目录的 jre/lib/ext 子目录(扩展目录)下加载类库

2.3.3应用程序类加载器

 Java 语言编写的,由 sun.misc.Launcher$AppClassLoader 实现. 派生于 ClassLoader 类.

 加载程序中自己开发的类

2.3.4自定义加载器

2.4双亲委派机制

加载一个类时,先委托给父类加载器加载,如果父类加载器没有找到,继续向上级委托,直到引导类加载器。

父级找到就返回,父级如果没有找到,基于委派给子级加载器,最终没有找到,报ClassNotFoundException.

 为了先确保加载系统类

双亲委派机制,是java提供的类加载的规范,但不是强制不能改变的。

可以通过自定义的类加载器,改变加载方式。

打破双亲委派机制:

可以通过继承ClassLoader类,重写loadClass/findClass方法,实现自定义的类加载

典型的tomcat中,加载部署在tomcat中的项目时,就使用的是自己的类。

三、运行时数据区

3.1程序计数器

是一块很小的内存空间,用来记录每个线程运行的指令位置,是线程私有的,每个线程都拥有一个程序计数器,声明周期与线程一直,是运行时数据区中,唯一一个不会出现内存溢出的空间。

运行速度最快。

3.2.本地方法栈

用来运行本地方法的区域

是线程私有

内存空间大小可以调整

可能会出现栈溢出

3.3java栈

基本作用:

  栈是运行单位,管理方法的调用。

  是用来运行java方法的区域。

  可能会出现栈溢出。

  是线程私有的。

运行原理:

  先进后出

  最顶部的称为当前栈帧

栈帧结构:

  一个栈帧中包含:

    局部变量表(存储在方法中声明的变量)

    操作数栈(实际计算运行)

    动态链接

程序计数器,java栈,本地栈是线程私有的

程序计数器不会出现内存溢出

java栈,本地栈可能会出现内存溢出

java栈,本地栈大小是可以调整的

3.4堆

基本作用特征:

  是存储空间,用来存储对象,是内存空间最大的一块区    域,

  在JVM启动时就被创建,大小可以调整(JVM调优)。

  本区域是存在垃圾回收的,是线程共享的区域。

堆空间的分区:

  年轻代(新生区/新生代)

    伊甸园区(对象刚刚创建存储在此区域)

    幸存者1

    幸存者2

  老年代(老年区)

为什么要分区

  可以根据对象的存活时放在不同的区域,可以区别对待。

  频繁回收年轻代,较少回收老年代。

创建对象,在堆内存中分布

  1.新创建的对象,都存储在伊甸园区

  2.当垃圾回收时,将伊甸园中垃圾对象直接销毁,将存活的对象,移动到幸存者1区

  3.之后创建的新对象还是存储在伊甸园区,当垃圾回收到来时,将伊甸园中的存活对象移动到幸存者2区,同样将性幸存者每次保证一个幸存者区为空的,相互装换。

  4.每次垃圾回收时,都会记录此对象经历的垃圾回收次数,当一个对象经历过15此回事,任然存活,就会被移动到老年代

  垃圾回收次数,在对象头中有一个4bit的空间记录,最大值只能是15;

  5.老年区回收次数较少,当内存空间不够用时,才回去回收老年代。

堆空间的配置比例:

默认的新生代与老年代的比例:1:2,可以通过-XX:NewRatio=2进行设置

如果项目中生命周期长的对象较多,就可以把老年代设置更大。

新生代中,伊甸园和两个幸存者区比例:8:1:1,

可以通过-XX:SurvivorRatio=8进行设置。

对象垃圾回收年龄:-XX:MaxTenuringThreshhold=<N>

分代收集思想Minor GC、Major GC、Full GC

对年轻代进行垃圾回收称为Minor GC/yong GC 是频繁进行的回收;

对老年代进行垃圾回收称为Major GC/old GC 回收次数较少;

Full GC 整堆收集,尽量避免;

System.gc();时

老年区空间不足时;

方法区空间不足时;

字符串常量池:

在jdk7之后,将字符串常量池的位置从方向区转移到了堆空间中,因为方法区的回收在整堆收集时发生,回收频率低;

3.5方法区

作用:主要用来存储加载的类信息,以及及时编译期编译后的信息,以及运行时常量池;

特点:在jvm启动时创建,大小也是可以调整,是线程共享,也会出现内存溢出;

方法区、堆、栈交互关系:

方法区存储来信息(元信息)

堆中存储创建的对象

栈中存储对象引用

方法区大小设置:

-XX:MetaspaceSize设置方法区的大小

Windows jdk默认值的大小是21MB

也可以设置为-XX:MaxMetaspaceSize的值是-1,没有限制,就可以使用计算机内存;

可以将初始值设置较大一点,减少FULL GC发生

方法区的内部结构:

类信息

以及即时编译后的信息

以及运行时常量池(指的就是类中各个元素的编号)

方法区的垃圾回收:

在FULL GC时方法区发生垃圾回收

主要是回收类信息,类信息回收条件比较苛刻,满足一下3点即可:

1.在堆中,该类及其子类的对象都不存在了

2.该类的类加载器不存在了

3.该类的Class对象不存在了

也可以认为类一旦被加载就不会被卸载了。

特点总结:

程序计数器,java栈,本地栈是线程私有的

程序计数器不会出现溢出

java栈,本地栈,堆,方法区可能会出现内存溢出

java栈,本地栈,堆,方法区大小是可以调整的

堆,方法区是线程共享的,是会出现垃圾回收的

本地方法接口:

什么是本地方法:

  用native关键字修饰的方法称为一个本地方法,没有方法  体。hashCode();

为什么用本地方法:

  java语言需要与外部的环境进行交互(例如需要访问内存,  硬盘,其他的硬件设备),直接访问操作系统的接口即可。

  java的jvm本身开发也是在底层使用到了C语言

执行引擎:

作用:将加载到内存中的字节码(不是直接运行的机器码),解释/编译为不同平台的机器码;

Java--编译--.class 在开发期间,由JDK提供的编译器(javac)进行源码编译(前端编译);

.class(字节码)-->解释/编译-->机器码(后端编译,在运行时,由执行引擎完成的)

翻译器:将字节码逐行解释执行

编译器(JIT即时编译器):将字节码编译,缓存起来,执行  更高效,不会立即使用编译器,将一些频繁执行的热点代码    进行编译,并缓存到方法区中,以后执行效率提高了。

  程序启动后,先使用解释器立即执行,省去了编译时间;

  程序运行一段时间后,对热点编译缓存,提高后续执行效率

采用的解释器和编译器结合的方案。

垃圾回收:

概述:

java是支持自动垃圾回收,有些语言不支持需要手动。

自动垃圾回收不是java语言首创的

垃圾回收关系的问题:

哪些区域需要回收   堆   方法区

什么时候回收

如何回收

java自动回收

java的自动垃圾回收经过长时间的发展,已经非常强大;

什么样的对象是垃圾

在运行过程中,平没有被任何引用指向对象,被称为垃圾对象。

为什么需要GC

如果不及时清理这些垃圾对象,会导致内存溢出。

在回收时,还可以将内存碎片进行整理(数组必须是连续空间的)

内存溢出和内存泄漏

内存溢出:经过垃圾回收后,内存中任无法存储创建的对象,内存不够用溢出;

内存泄漏:IO流close   jdbc连接close没有关闭,声明周期很长的对象,一些已经不用的对象,但是垃圾回收器不能判定为垃圾,这些对象就默默的占用的内存,称为内存泄漏,大量的此类对象存在,也是导致内存溢出的原因;

自动内存管理:

好处:解放程序员,对内存管理更合理,自动化;

缺点:对程序员管理内存的能力降低了,解决问题能力变弱了,不能调整垃圾回收的机制;

垃圾回收相关算法:

标记阶段:

作用:判断对象是否为垃圾对象,是否有引用指向对象。

相关的标记算法:引用计数算法和可达性分析算法

引用计数算法(在现代的jvm中并没有被使用)

有个计数器来记录对象的引用数量

String s1 = new String("aaa");

String s2 = s1;  //有两个引用变量指向aaa对象

s2 = null; -1

s1 = null; -1

缺点:需要维护计数器,占用空间,频繁操作需要事件开销;

无法解决循环问题。多个对象之间相互引用,没有其他哇爱不引用指向他们,计数器都不为0,不能回收,产生内存泄漏。

可达性分析算法/根搜索算法

实现思路:从一些为根对象(GCRoors)的对象触发去寻找,与根据对象直接或间接连接的对象就是存活对象,不与根对象引用链连接的对象就是垃圾对象。

GCRoots可以是哪些元素的?

在虚拟机栈中被使用的。

在方法中存储的静态成员指向的对象

作为同步锁使用的synchronized

在虚拟机内部使用的对象

对象的finalization机制

当一个对象被标记为垃圾后,在真正被回收之前,会调用一次Object类中finalize(),留给垃圾回收器调用。

有了finalization机制的存在,在虚拟机中把对象状态分为3种:

1.可触及的不是垃圾,与根对象连接的

2.可复活的 判定为垃圾了,但是还没有电源finalize(),在finalize()中对象可能会复活

3.不可触及的:判定为垃圾了,finalize()也被执行过了,这种就是必须被回收的对象;

垃圾回收阶段的算法:

标记--复制算法:

将内存分为大小相等的两份空间,把当前使用的空间中存活的对象复制到另一个空间中,将正在使用的空间中垃圾对象清除。

优点:减少内存碎片

缺点:如果需要复制的对象数量多,效率低。

使用场景:存活对象少,新生代适合标记复制算法;

标记--清除算法:

清除不是真正的把垃圾对象清除掉,将垃圾对象地址维护到一个空闲列表中,后面有新对象到来时,覆盖掉垃圾对象即可。

特点:

实现简单

效率低,回收后有碎片产生

标记--压缩算法(标记-整理)

垃圾回收器:

垃圾收集器是垃圾回收的实际实现者,垃圾回收算法是方法论。

垃圾回收器分类:

按线程分类:

单线程垃圾回收器:

Serial      serial  old

多线程垃圾回收器:

Parallel   

按照工作模式分类:

独占式:垃圾回收线程执行时,其他线程暂停

并行式(并发式):垃圾回收线程可以和用户线程同时执行

按工作的内存分类:

年轻代垃圾回收器

老年代垃圾回收器

垃圾回收器性能指标:

吞吐量

暂停时间

回收的速度

占用内存大小

CMS垃圾回收器

Concurrent  Mark  Sweep  并发标记清除器

支持垃圾回收线程与用户线程并发(同时)执行

初始标记:独占式的暂停用户线程

并发标记:垃圾回收线程与用户回收线程(同时)执行

重新标记:独占式的暂停用户线程

并发清除:垃圾回收线程与用户线程并发(同时)执行,进行垃圾对象的清除;

优点:可以做到并发收集

缺点:使用标记清除算法,会产生内存碎片,并执行影响到用户线程,无法处理浮动垃圾;

三色标记:

由于cms有并发执行过程,所以在标记垃圾对象时有不确定性。

所以在标记时,将对象分为3种颜色(3种状态)

黑色:例如GCRoots确定是存活的对象

灰色:在黑色对象中关联的对象,其中还有未扫描完的,之后还需要再次进行扫描;

白色:与黑色,灰色对象无关联的,垃圾收集算法不可达的对象;

标记过程:

1.先确定GCRoots,把GCRoots标记为黑色

2.与GCroots关联的对象标记为灰色

3.再次遍历灰色,灰色变为黑色,灰色下面有关联的对象,关联的对象变灰色

4.最终保留黑色,灰色,回收白色对象

G1(Garbage-First) 垃圾优先

 将堆内存各个区又分成较小的多个区域, 对这些个区域进行监测,对某个区域中垃圾数量大的区域优先回收.

也是并发收集的.

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叫我剑锋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值