JVM简单了解

一、JVM概述

1.jvm的作用

将.class字节码文件装载,编译解释为该平台对应的机器码。jvm不仅可以加载java的字节码文件还可以加载其他语言的字节码文件。

2.jvm的组成

jvm的整体组成分为以下四个部分

1.类加载器(ClassLoder)

2.运行时数据区(Runtime Data Area)

3.执行引擎(Excute Engine)

4.本地库接口(Native Interface)

2.1类加载

类加载系统从文件系统或网络中加载class文件,而是否可以运行由执行引擎决定。

2.1.1加载

使用流将硬盘上的字节码读取到内存中(运行时数据区)的方法区中,生成对象的class对象

2.1.2链接

验证:验证字节码格式是否正确,检查class字节码文件是否被篡改

准备:为静态变量赋默认值,如static int a = 123;此时a被赋予int类型默认值0;

解析:将字节码中符号引用替换成直接引用,(符号引号就是字节码中的逻辑符号,直接引用就是内存地址)

2.1.3初始化

当类完成初始化,即代表这个类加载完成。

初始化过程中主要有以下功能,1.初始化类中静态变量;2.执行静态代码块;如果有父类,则从父类开始递归初始化。初始化过程中是在执行底层构造方法<client>(),该方法是由编译器生成的。

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

1.使用类中的静态成员,如调用类中的静态方法,静态变量

2.运行类中的main方法

3.new该类的对象

4.加载该类的子类

5.通过反射机制 Class.forName()

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

1.使用类中的静态常量

2.使用该类创建一个数组对象

2.1.4类加载器分类

引导类加载器(启动类加载器,bootstrap classloder)

这个类加载器用语言和C++实现,嵌套在jvm内部,负责加载java核心库中的类。如java.lang、java.util、java.math、java.io、java.sql等包中的类。

扩展类加载器(extension classloder)

这个类加载器由java语言实现,用于加载java8\jre\lib\ext 目录下的类

应用程序类加载器(application classloder)

这个类加载器由java语言实现,用于加载我们自定义的类。

2.1.5双亲委派机制

该机制是指在类加载过程中,会先由上级的类加载器进行加载,如果找不到该类,则逐级由下面的类加载器加载,如果找不到类则抛出ClassNotFoundException异常。该机制是必要的,可以有效地防止用户自定义的类因为地址名重复而覆盖源代码的类。

2.2运行时数据区

**作用:**运行时数据区是java程序运行时,jvm存储数据和管理程序执行的内存区域。

虽然各虚拟机的运行时数据区略有不同,但是都是满足java虚拟机的规范的,java8虚拟机规定,运行时数据区包括以下区域:1.方法区;2.堆;3.程序计数器;4.本地方法栈;5.虚拟机栈

2.2.1程序计数器

作用: 程序计数器是用来记录当前线程中执行的指令的位置

程序计数器特点:

程序计数器是用来记录当前线程中执行的指令的位置,因此每个线程都有一个程序计数器,程序计数器是线程私有的;

程序计数器占用内存小,执行速度快;

程序计数器随线程而生,随线程而死;

程序计数器不存在内存溢出的情况。

2.2.2虚拟机栈

**作用:**虚拟机栈是java执行方法的区域

虚拟机栈的特点:

虚拟机栈中程序可以执行java方法,属于运行结构,每个线程都会有一个虚拟机栈,因此虚拟机栈也是线程私有的。

当栈中执行的方法过多时,会出现栈溢出异常。

栈运行特点,先进后出/后进先出

方法被调用过后,在栈中称为栈帧 ,栈帧内部包括局部变量表,表达式栈,方法返回地址

局部变量表 中存储方法中的参数和方法内部的局部变量,基本数据类型直接存值,引用类型则存储对象的引用。

表达式栈 进行方法中的所有计算过程。

当一个方法执行完毕之后,要返回之前调用它的地方,因此在栈帧中必须保存一个方法返回地址

2.2.3本地方法栈

**作用:**本地方法栈主要用来运行被调用的本地方法(用native修饰的方法,由操作系统实现的c++/c语言方法)

2.2.4java堆内存

**作用:**堆是用来存储运行时产生的所有对象。

堆的特点:

堆是java内存管理的最大区域,是jvm中内存最大的区域。堆的内存大小是可以调节的(运行时数据区中除了程序计数器的大小不可以调整以外,其他均可以通过jvm调优来调整大小)。例如:-Xms:10m(堆起始大小)-Xmx:30m(堆最大内存大小)。

java产生的所有对象都在堆中存储

所有的线程共享堆内存

堆空间会发生内存溢出

堆中是垃圾回收的主要区域

堆的空间分区:

堆分为新生区和老年区,新生区又分为伊甸园区和幸存者区,幸存者区又分为幸存者0区和幸存者1区。

对象在堆中的分配过程: 新产生的对象首先存放在伊甸园区,在第一次GC后,所有未被回收的对象会被转移到幸存者0区;在第二次GC后,伊甸园区和幸存者0区的所有对象会被转移到幸存者1区;再次进行GC后,伊甸园区和幸存者1区的所有对象都会转移到幸存者0区,如此循环往复,直到对象的GC次数超过15次,就会被转移到老年区,老年区的对象就相对稳定,GC的频率会较低。或者当对象较大时也会被送到老年区。

**堆空间分区的必要性:**对堆空间进行分区是必要的,通过对对象在堆中进行分区,来调整GC对对象的扫描频率,从而提高GC的效率。

设置堆空间的参数:

-Xms:初始堆空间内存

-Xmx:最大堆空间内存

-Xmn:设置新生代的大小

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

2.2.5方法区

**作用:**方法区主要用来储存加载到内存中的类信息,也会存储静态方法和变量及即时编译器编译的代码

方法区特点: 方法区物理上和堆处于同一个空间,但是逻辑上会对其进行区分,我们又称方法区为元空间

方法区大小是可以设置的,通过对:MetaspaceSize=<N>参数进行修改, 方法区一旦空间不足,会触发FULL GC(整堆收集),会影响应用程序线程,一般情况下,可以把方法区设置较大一点

方法区存在内存溢出

方法区存在垃圾回收

方法区中的类什么时候会被回收?

想要对方法区中的类进行回收,需要满足以下三个条件:

1.方法区中该类及其所有子类的实例都不存在

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

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

2.3本地方法库接口

**作用:**本地库接口是一个编程框架,可以让java代码与其他语言编写的代码进行交互,即让java程序调用本地方法(c语言或c++)。

**什么是本地方法:**本地方法就是用native关键字修饰的方法

**为什么要调用本地方法:**本地方法可以与操作系统上的硬件进行交互,而java属于应用程序语言,没有权限操作硬件设备。

java 复制代码
new Object().hashCode(); //public native int hashCode();  获取内存地址
new FileInputStream("").read();  //private native int read0() 读硬盘数据
new Thread().start();//    private native void start0(); 把线程注册到操作系统
2.4执行引擎

**作用:**执行引擎(excution engine)的作用是将字节码文件解释/编译为对应平台上的机器语言。其充当了高级语言和机器语言之间的翻译官。

2.4.1解释器

解释就是对字节码逐行解释成机器码的过程,该过程响应速度快,程序运行就可以投入使用,但是逐行解释效率较低。

2.4.2JIT编译器

编译就是对字节码整体编译后执行,编译需要花费一定时间,但是效率高。JIT编译器会对热点代码进行编译后缓存,以后使用时就不需要进行编译操作了。

2.4.3半解释半编译

java是半解释半编译语言,这样设计既可以在程序运行之处利用解释的特点立即执行,也能通过编译操作提升效率。

2.5垃圾回收

什么是垃圾: 垃圾就是程序运行过程中没有任何引用能访问到的对象。
**清理垃圾:**如果不及时对垃圾进行清理,就会导致堆内存溢出。因为垃圾会一直占据内存知到程序运行结束。

内存溢出: 内存溢出就是内存不够用了,但是还有新的对象产生,于是程序抛出异常。
**内存泄漏:**内存泄漏是因为某些对象程序已经不再使用了,但因为某些原因仍存在应用,GC无法对其进行回收,例如数据库连接对象,网络Socket对象。

**Stop The World(STW):**在GC过程中,会将应用程序的线程停止,发生短暂的卡顿。

finalize机制: java中的Object类提供了一个finalize()方法,这个方法是在对象被判定为垃圾后,在被回收之前自动被垃圾回收器调用的方法。该方法是针对对象的,只能被调用一次。如果在第一次的finalize()方法中将本标记为垃圾对象复活(为其重新指向引用),则第二次垃圾回收时就不会调用finalize()方法了。

对象的分类:

1.可触及:没有被判定为垃圾对象

2.可复活:被判定为垃圾对象,但是还没调用过finalize()方法。

3.不可触及:第二次被判定为垃圾对象。

2.5.1早期的垃圾回收

早期的C和C++都是手动申请内存,手动释放内存。此种设定可以精确地管理内存,使用时申请,不需要时就释放。但是会给程序员带来较大的负担,一旦忘记释放内存,就会产生内存泄漏。

2.5.2java的垃圾回收

java采用的是自动垃圾回收,解放了程序员,不用再关心什么时候释放空间。

2.5.3垃圾回收的区域

堆是垃圾回收的重点区域,对堆中的新生代会频繁进行GC操作,较少对老年区GC。方法区因为类加载信息回收较为苛刻,需要等到FULL GC(整堆回收)。

2.5.4垃圾回收算法

++2.5.4.1垃圾标记阶段的算法++

**1.引用计数算法(Reference Counting)**对每个对象都有一个引用计数的属性,当引用计数为0时,表示该对象已不存在引用指向他,便标记为垃圾。然而该方法有个致命缺点,就是无法解决循环引用问题,从而导致内存泄漏。

2.可达性分析算法 该方法从活跃对象开始向下寻找,与活跃对象相连接的都是被使用的,而未与活跃对象连接的就是垃圾对象。

活跃对象包括:

1.虚拟机栈中方法使用的对象;

2.引用类型静态变量

java 复制代码
static Object globalVariable = new Object(); // 静态变量是GC Root

3.所有被同步锁持有的对象

4.jvm内部的引用对象,如常驻的异常对象,类加载器等。

++2.5.4.2正式回收阶段算法++

1.标记复制算法:

标记复制算法可以有多个内存空间 ,会将区域中的有用对象集体复制 到其他区域中,然后将原本区域中的对象全部清除 。通常适用于回收新生区。因为新生区中垃圾较多,需要复制的对象较少。

2.标记清除算法

该算法只需要一块内存,会将被标记为垃圾的对象直接清除,但是回收后会导致内存碎片化,适用于老年区,老年区中垃圾较少。

3.标记压缩算法

该方法只需要用到一块内存,会将被标记的对象删除后,再对剩余对象进行移动,防止产生碎片化区域。

分代收集

年轻代的对象,存活的少,垃圾多,使用标记复制算法进行回收

老年代的对象大,且存活时间长,采用标记清除和标记压缩算法进行回收

优化垃圾回收效率

2.5.5垃圾回收器

1.分类:

垃圾回收器按线程数分为:

单线程(串行)Serial

多线程(并行)Parallel 垃圾回收器。

从工作模式上分为:

独占式的: 当垃圾回收线程在执行时,其他用户线程暂停(STW)

并发式的: 垃圾回收线程和用户线程可以同时执行, 减少了用户线程暂停的次数和时间,并未完全不存在STW。

按照工作内存分为:

新生代垃圾收集器

老年代垃圾收集器

jdk8支持的垃圾回收器:

相关推荐
探索未来 航行现在1 分钟前
Go语言--语法基础4--基本数据类型--整数类型
开发语言·后端·golang
潘越越7 分钟前
Maven和MyBatis学习总结
java·maven·mybatis
喵手10 分钟前
Java中set集合常用的stream方法,你了解哪几种?
java·后端·java ee
花花鱼11 分钟前
js中 剩余运算符(Rest Operator )(...)和展开运算符(Spread Operator)(...)的区别及用法
开发语言·javascript·ecmascript
sxf35912 分钟前
vue项目调用netcore webapi接口提示:400 Bad Request的解决
java·前端·vue.js
LiuYaoheng15 分钟前
Java新手村第二站:泛型、集合与IO流初探
java·学习
ゞ 正在缓冲99%…18 分钟前
leetcode167.两数之和||
java·算法·leetcode·双指针
泽55318033 分钟前
4.13日总结
开发语言·python·pycharm
键盘不能没有CV键33 分钟前
【日志链路】⭐️SpringBoot 整合 TraceId 日志链路追踪!
java·git·intellij-idea
路在脚下@41 分钟前
RabbitMQ惰性队列的工作原理、消息持久化机制、同步刷盘的概念、延迟插件的使用方法
java·rabbitmq