JVM重点知识总结

1.运行时数据区(内存模型)

线程不安全: 方法区

线程安全 :虚拟机栈本地方法栈程序计数器

程序计数器:较小的内存空间,是当前线程执行的字节码行号知识器,改变计数器的值来选取下一条需要执行的字节码指令(分支、循环、跳转)

虚拟机栈:java方法执行的内存模型,每个方法执行都会创建一个栈帧,里面有局部变量表(八中基本数据类型和对象引用),操作数栈(运行结果),动态链接和方法出口等,方法开始和结束就是栈帧入栈和出栈的过程。

本地方法栈:与虚拟机栈类似,为虚拟机使用到的本地Native方法(C语言)服务。

Java堆:内存最大的一块,在虚拟机启动时创建,几乎所有的对象实例都在这里分配内存 (随着JIT编译器的发展,在编译期间,如果JIT经过逃逸分析,发现有些对象没有逃逸出方法,那么有可能堆内存分配会被优化成栈内存分配。)

方法区:存储被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据

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

直接内存:JDK1.4后加入了NIO类,引入了一种基于通道与缓冲区的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过有一个存储在java堆中的对象,作为这块内存的引用进行操作

 

这里需要说明一下对象访问的方式,主要包括2种句柄访问和直接指针访问:

 1. 句柄访问主要是Java堆中划分一块句柄池,虚拟机栈中存放句柄池中的地址,句柄池中包括对象的实例数据和对象类型的数据的地址

 2.直接指针访问,就是虚拟机栈直接指向Java堆中的对象类型指针和对象的实例数据,然后对象类型指针在指向方法区中对象类型的实例数据

Student s = new Student();在内存中做了哪些事情?

• 加载Student.class文件进内存

• 在栈内存为s开辟空间

• 在堆内存为学生对象开辟空间

• 对学生对象的成员变量进行初始化

• 通过构造方法对学生对象的成员变量赋值

• 学生对象初始化完毕,把对象地址赋值给s变量

 

2.内存溢出

①java堆溢出----OutofMemoryError异常

S1=new ArrayList();

While(true){

S1.add(new Person());

}

结果:虚拟机将当前的堆导出,并保存到XXX.dump文件下。使用MAT工具可以分析

②栈溢出(本地/虚拟机)

private static int  stackLength=1;

Public void stackLeak(){

stackLength ++;

stackLeak();-------不停的递归调用这个函数,线程请求栈的深度大于虚拟机栈所允许的深度,栈就会溢出

}

③方法区和常量池溢出

String.intern()是一个native方法,如果字符串常量池中已经包含有一个等于此String字符串,则返回代表池中这个String对象。使用list保持着常量池引用,避免FullGC回收常量池行为

Int i=0;

While(true){

List.add(String.ValueOf(i++).intern());------自动装箱,拆箱intValue()

}

 

3.判断对象死亡?

①引用计数法:添加引用计数器,每当有一个引用,计数器值加一;当引用失效,计数器值减一,为0时就不能被使用

*不能解决循环引用的例子

②可达性分析:节点向下搜索,走过路径称为引用链(GC ROOT)

·虚拟机栈引用的对象(栈帧中的本地变量表)

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

·方法区中常量引用的对象

·本地方法栈中的引用的对象

 

宣告死亡:有两次标记过程,在finalize()中可以重新关联对象

 

四大引用:

强引用:Object o=new Object();

软引用:有用但非必须,列进回收范围进行二次回收

弱引用:被关联的对象只能生存到下一次垃圾回收之前

虚引用:不影响也不能使用(目的,回收会提示)

 

4.JVM垃圾处理方法(标记清除、复制、标记整理)

• 标记-清除算法 ①标记阶段:先通过根节点,标记所有从根节点开始的对象,未被标记的为垃圾对象 ②清除阶段:清除所有未被标记的对象

---缺点:产生大量不连续的内存片段,空间资源浪费

 

• 标记-整理 ①标记阶段:先通过根节点,标记所有从根节点开始的可达对象,为被标记的为垃圾对象 ② 整理阶段:将所有的存活对象压缩到内存的一段,之后清理边界所有的空间

 

• 复制算法 ①将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,然后清除正在使用的内存块中的所有对象。

Java堆中内存分配

新生代与老年代其中新生代分为eden:from:to=8:1:1  采用复制算法

Eden和from区第一次进行minorgc(复制算法)后存活对象到to,清空Eden和from区域,然后from,to交换。内存不够进行担保分配、多次gc存活年龄很大、大对象进入到老年代。老年代快满时进行fullGC(标记清除/整理)

 

5.垃圾收集器

①CMS:基于”标记-清除”算法实现,并发标记,并发清除,速度非常快---缺点:产生大量的空间碎片

②G1:基于”标记-整理”算法,并行与并发,分代收集,空间整理,吞吐率高

③Serial:单线程收集器,在垃圾收集时,必须暂停其他工作线程,直到结束

优点:简单而高效(相对单线程收集器)、没有线程交互的开销,专心垃圾回收,获得效率

 

6.JVM类加载

加载-->验证-->准备-->解析-->初始化-->使用--->卸载

通过全限定名来加载生成class文件对象到内存中,然后进行验证这个class文件,包括文件格式、元数据、字节码检验。准备是对这个对象分配内存,解析是将符号引用转化为直接引用。初始化就是开始执行构造器的代码

初始化中注意①new关键字实例化对象、读取或设置一个类的静态字段(除被final修饰、已在编译期放入常量池的静态字段),以及调用一个类的静态方法的时候②对类进行反射调用时③先初始化父类,在初始化子类④虚拟机启动用户指定执行的主类

 

双亲委派概念 :如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的加载器都是如此,因此所有的类加载请求都会传给顶层的启动类加载器,只有当父加载器反馈自己无法完成该加载请求(该加载器的搜索范围中没有找到对应的类)时,子加载器才会尝试自己去加载。

 

优点:保证程序稳定运行,一个java.lang.Object无论哪一个类加载器加载这个类,最终都委托给顶端的启动类加载器,保证Object类在程序的各种类加载器环境中都是同样一个类,相反没有双亲委派模型,系统会出现多个不同的Object类,混乱。

 

 • 加载器

启动(Bootstrap)类加载器(c++虚拟机自身)

扩展(Extension)类加载器

应用程序(Appliction)类加载器

自定义(User)类加载器

 • 如果加载同一个类,该使用哪一个类? ○ 父类的

 

7.JVM性能调优

一些运行参数:

-Xmx:堆内存最大限制

-Xms:起始内存大小

-XXNewSize:新生代大小

-XXNewRatio:新生代与老年代比例

 -XX:SurvivorRatio:eden:from:to比例

通过参数-XX:+HeapDumpOnOutOfMemoryError可以让虚拟机在出现内存溢出异常时Dump出当前的内存堆转储快照分析。一般通过内存映像分析工具(如Eclipse Memory Analyzer、EclipseMAT)对Dump出来的进行分析,先查看是内存泄漏还是内存溢出

内存泄漏:可以通过工具查看泄漏对象到GCRoot的引用链,就能追踪到为什么垃圾收集器无法回收;内存溢出:调整虚拟机堆参数(Xmx,Xms)

排错:用jmap内存映像工具)看内存情况,jstack(堆栈跟踪工具)看某个新java进程内线程堆栈信息

 JVM线程死锁,你该如何判断是因为什么?如果用VisualVM,dump线程信息出来,会有哪些信息

• 常常需要在隔两分钟后再次收集一次thread dump,如果得到的输出相同,仍然是大量thread都在等待给同一个地址上锁,那么肯定是死锁了。

----------参考文献《深入理解java虚拟机》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值