1.JVM介绍

1.什么是jvm

Java Virtual Machine(java二进制字节码运行环境) 好处:

一次编译,好处运行自动内存管理,垃圾回收机制数组下标越界检查多态 比较JVM\JRE\JDK

jvm=屏蔽java代码与底层操作系统的差异 JRE=JVM+基础类库 JDK=JVM+JRE+编译工具 Java -v -p class 反编译字节码

2.常见jvm

3.jvm运行流程

javaClass被编译成二进制字节码后通过类加载器加载到jvm里面运行。类放到方法区,类创建的实例对象放到堆中,堆里面的对象调用方法时会使用到虚拟机栈,程序计数器、本地方法栈。方法执行时每行代码通过执行引擎中的解释器逐行执行,方法里的热点代码会由即时编译器做一个优化后的执行,堆中没有被引用的对象会被垃圾回收,java不方便实现的功能必须调用底层的方法就需要调用本地方法提供的接口

2.内存结构

1.程序计数器

定义 程序计数器是java对物理硬件的屏蔽和抽象,物理上是通过寄存器来实现的。 首先将java源码编程成二进制字节码(这里统一简称jvm指令),然后解释器将每一条指令解释成机器码,最后交给cpu执行,执行过程中寄存器会记录下一条指令的地址。 作用

记录下一条jvm指令的执行地址 特点

是线程私有的,每一个线程都有属于自已的寄存器不会存在内存溢出

2.栈

什么是栈?

栈是一种先进后出的结构,是线程运行需要的内存空间,它有很多栈帧组成,栈帧是方法运行所需要的内存空间,它里面包含方法的参数、局部变量、返回值垃圾回收不涉及栈内存栈内存并不是分配(-xss)的越大越好,分配越大(比如物理内存有500m -xss设置2m,那么线程数就为250),线程数越少,不过可以更多次的递归调用

cpu占用过高如何排查?

命令

使用top命令查询到哪个进程占用cpu最高ps -H(进程里所有的线程信息展示出来) -eo pid,tid,%cpu | grep 进程id 用ps命令进一步查看哪个线程引起的cpu过高 jstack 进程id

根据线程id(jstack查询来的线程是10进制显示) 找到有问题的线程,进一步定位到问题代码

3.本地方法栈

虚拟机调用本地方法时,需要给本地方法提供的内存空间,因为java代码有一些限制不会和操作系统底层打交道,所以需要c或者c++这样的语言编写的本地方法与操作系统api打交道,java代码可以间接调用本地方法来调用到底层方法

4.堆

定义

通过new关键字,创建对象都会使用堆内存 特点

线程共享,堆中的对象都需要考虑线程安全的问题有垃圾回收机制 堆内存溢出 堆内存诊断

jps

查看当前系统中有哪些java进程 jmap工具

查看堆内存占用情况 jamp -heap 进程id jconsole工具

图形界面,多功能的监测工具,可以连续监测 jvirsualvm工具

图形界面

5.方法区

定义 方法区是所有java虚拟机线程共享的区,存储了跟类的结构相关的信息(成员变量、方法数据、成员方法和构造器的代码),方法区在虚拟机启动时创建。方法区申请内存时发现不足,虚拟机会抛出OOM 常量池

就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息运行时常量池,常量是*.class文件中的,当该类被加载,它的常量池信息就会被放入运行时常量池,并将里面的符号地址变为真实地址 StringTable特性

常量池中的字符串仅是符号,第一次使用时才变为对象利用串池的机制,来避免重复创建字符串对象字符串拼接的原理是StringBuilder字符串常量值拼接的原理是编译器优化

可以使用intern方法主动将串池中还没有的字符串放入串池 stringTable会发生垃圾回收

6.直接内存

定义

常见于NIO操作时,用于数据缓冲区分配回收成本较高,读写性能高不受JVM内存回收管理 直接内存的基本使用(零拷贝)

bytebuffer

传统io 比对效率 原理图 直接内存的释放原理

当调用System.gc的时候,直接内存也被释放了,底层是怎么做的呢?

底层通过Unsafe对直接内存的分配,通过freeMemory来释放直接内容

3.垃圾回收

1.四种引用

强引用

只有所有GCroots对象都不通过【强引用】引用该对象,该对象才能被垃圾回收 软引用

仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次触发垃圾回收,回收软引用对象可以配合引用队列来释放软引用自身 // -Xmx20m

public class Demo1 {

private static final int _4MB= 4 * 1024 * 1024;

public static void main(String[] args) {

List> list = new ArrayList<>();

ReferenceQueue queue = new ReferenceQueue();

for (int i = 0; i < 5; i++) {

// 加入引用队列,垃圾回收时

SoftReference ref = new SoftReference<>(new byte[_4MB],queue);

System.out.println(ref.get());

list.add(ref);

System.out.println(list.size());

}

System.out.println("循环结束:"+list.size());

// 清除软引用

Reference poll = queue.poll();

while (poll != null){

list.remove(poll);

poll = queue.poll();

}

for (SoftReference ref : list) {

System.out.println(ref.get());

}

}

}

弱引用

仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象可以看到执行到14次的时候,弱引用全部为null,是因为发生了一次fullGC 虚引用

必须配合引用队列使用,主要配合ByteBuffer使用,被引用对象回收时,会将虚引用入队,由RefrenceHandler线程调用虚引用相关方法释放直接内存 终结者引用

无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用入队(被引用对象暂时没有被回收),再由Finalizer线程通过终结器引用找到引用对象并调用它的finalizer方法,第二次gc时才能回收被引用对象

2.垃圾回收算法

1.标记清除算法

扫描堆内存中没有被根对象所直接或者间接引用的对象进行标记,然后将标记的内存空间给释放(对象起始结束的地址放到一个空闲地址列表里)下次有新对象时到这个列表里找有没有足够的空间容纳新对象优点:速度快。缺点:内存碎片化

2.标记整理

优点:内存碎片化。缺点:速度慢

3.复制

优点:内存碎片化。缺点:双倍内存空间

3.分代回收

jvm不会只使用一种垃圾回收算法,而是会根据情况来选择使用,具体实现在jvm里面称之为分代的垃圾回收机制

首先新的对象被使用伊甸园空间当新生代空间不足时,会触发一次MinorGC将存活的对象使用复制算法复制到幸存区To中,并标记回收次数+1,然后交换幸存区to和from的位置minorGC会引发stop the word,暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行当对象寿命超过阈值,会晋升老年代,最大寿命是15(4bit)当老年代空间不足时,会先尝试minorGC,如果之后空间仍不足时,会触发FullGc,STW时间会更长

1.jvm参数

4.垃圾回收器

1.串行

单线程堆内存比较小,适合个人电脑新生代采用复制算法,老年代采用标记+整理

2.吞吐量

多线程堆内存较大,多核cpu让单位时间内,stw时间最短并行(多线程)新生代采用复制算法,老年代采用标记+整理

3.响应时间 CMS垃圾回收器

多线程堆内存较大,多核cpu单词stw时间最短

4.G1垃圾回收

适用场景

同时注重吞吐量、低延时,默认的暂停目标是200ms超大堆内存,会将堆内存划分为多个相同大小的region整体上是标记+整理算法,两个区域之间是复制算法 相关参数 -XX:+UseG1GC -XX:G1HeapRegionSize=size -XX:MaxGCPauseMillis=time 运行流程

YoungCollection

新生代收集,对象分配到伊甸园区,当伊甸园区满了后会触发新生代垃圾回收会STW并以拷贝的算法放到幸存区,幸存区满了会部分对象晋升老年代 YoungCollection+ConcurrentMark

在YoungGC时会进行GcRoot的初始标记老年代占用堆空间比例达到阈值时,进行并发标记(不会STW),由jvm参数决定 -XX:InitiatingHeapOccupancyPercent=percent(默认45%) MixedCollection

1.字符串去重

优点:节省内存空间 缺点:略微多占用了cpu空间,新生态回收时间略微增加

5.垃圾回收调优

1.新生代调优

新生代的特点

所有的new操作的内存分配非常廉价 死亡对象的回收代价是0 大部分对象用过即死 MinorGC时间远低于FullGC

2.老年代调优

cms为例

cms的老年代内存越大越好 先不做调优,如果没有fullgc说明老年代空间充裕,否则先尝试调优新生代 观察发生fullgc时老年代的内存占用,将老年代内存预设调大1/4~1/3 -XX:CMSInitiatingOccupancyFraction=percent

6.类文件结构

1.类文件结构

7.类加载

1.加载

将类的字节码载入方法区,内部采用c++的instanceKlass描述java类,它的重要field有:

_java_mirror即java的类镜像,例如对string来说,就是String.class,作用是把klass暴露给java使用_super即父类_fileds即成员变量_methods即方法_constants即常量池_class_loader即类加载器_vtable虚方法表_itable接口方法表 如果当前类的父类还没有加载,先加载父类加载和链接可能是交替运行的

2.链接

验证类是否符合JVM规范,java安全检查

可以将魔术部分修改进行测试 准备

static变量分配空间和赋值是两个步骤,分配空间在准备阶段完成,赋值在初始化阶段完成如果static变量是final的基本类型,以及字符串常量,那么编译阶段值就确定了,赋值在准备阶段完成如果static变量是final的引用类型,那么赋值也会在初始化阶段完成 解析

将常量池中的引用解析为直接引用

3.初始化

会初始化的情况

main方法所在的类,总会被首先初始化首次访问这个类的静态变量或静态方法时子类初始化,如果父类还没初始化子类访问父类的静态变量,只会触发父类的初始化Class.forNamenew 会导致初始化 不会初始化的情况

访问类的static final的静态常量(基本类型和字符串)类对象.Class创建该类的数组类加载器的loadClass方法Class.forName的参数2为false时

8.类加载器

1.类加载器介绍

推荐文章

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: