文章目录
- java内存区域
- 垃圾回收
- 类文件结构(.class文件)--非重点
- 类文件加载过程(虚拟机是如何加载这些 Class 文件的)
- 类加载器详解(双亲委派模型)
- JVM参数总结以及优化(linux指令待学)
java是编译+解释共存的语言
.class文件(jvm可读的二进制文件)和JVM生成的可读的字节码(指令)文件的区别
.class文件(16进制)
字节码文件(可阅读的指令文件)
注意:程序计数器指向的就是这个字节码指令的每行的运行的位置
Java虚拟机栈异常的情况
注意:一般Java虚拟栈一般在线程运行时是不会出现栈内存的溢出的,除非创建新线程申请内存时因内存不足就直接抛出OOM。运行时,一般只会抛出stackOverFlow的异常,就是超过了栈的深度,新的栈帧已经无法入栈
针对死归,会出现stackoverFlow
针对创建过多的线程出现栈内存溢出的情况
堆内存异常情况
针对堆内存OOM可以解决分析的步骤:
内存泄漏和内存溢出
注意:内存泄露会最终导致内存溢出
GC日志打印
[0.008s][info][gc] Using G1
[0.009s][info][gc,init] Version: 16.0.2+7-67 (release)
[0.009s][info][gc,init] CPUs: 8 total, 8 available
[0.009s][info][gc,init] Memory: 16167M
[0.009s][info][gc,init] Large Page Support: Disabled
[0.009s][info][gc,init] NUMA Support: Disabled
[0.009s][info][gc,init] Compressed Oops: Enabled (Zero based)
[0.009s][info][gc,init] Heap Region Size: 2M
[0.009s][info][gc,init] Heap Min Capacity: 8M
[0.009s][info][gc,init] Heap Initial Capacity: 254M
[0.009s][info][gc,init] Heap Max Capacity: 4042M
[0.009s][info][gc,init] Pre-touch: Disabled
[0.009s][info][gc,init] Parallel Workers: 8
[0.009s][info][gc,init] Concurrent Workers: 2
[0.009s][info][gc,init] Concurrent Refinement Workers: 8
[0.009s][info][gc,init] Periodic GC: Disabled
[0.009s][info][gc,metaspace] CDS archive(s) mapped at: [0x0000000800000000-0x0000000800bb0000-0x0000000800bb0000), size 12255232, SharedBaseAddress: 0x0000000800000000, ArchiveRelocationMode: 0.
[0.009s][info][gc,metaspace] Compressed class space mapped at: 0x0000000800c00000-0x0000000840c00000, reserved size: 1073741824
[0.009s][info][gc,metaspace] Narrow klass base: 0x0000000800000000, Narrow klass shift: 3, Narrow klass range: 0x100000000
Java HotSpot(TM) 64-Bit Server VM warning: Options -Xverify:none and -noverify were deprecated in JDK 13 and will likely be removed in a future release.
[0.477s][info][gc,start ] GC(0) Pause Young (Normal) (G1 Evacuation Pause)
[0.477s][info][gc,task ] GC(0) Using 6 workers of 8 for evacuation
[0.480s][info][gc,phases ] GC(0) Pre Evacuate Collection Set: 0.0ms
[0.480s][info][gc,phases ] GC(0) Merge Heap Roots: 0.0ms
[0.480s][info][gc,phases ] GC(0) Evacuate Collection Set: 2.9ms
[0.480s][info][gc,phases ] GC(0) Post Evacuate Collection Set: 0.4ms
[0.480s][info][gc,phases ] GC(0) Other: 0.3ms
[0.480s][info][gc,heap ] GC(0) Eden regions: 11->0(13)
[0.480s][info][gc,heap ] GC(0) Survivor regions: 0->2(2)
[0.480s][info][gc,heap ] GC(0) Old regions: 0->1
[0.480s][info][gc,heap ] GC(0) Archive regions: 0->0
[0.480s][info][gc,heap ] GC(0) Humongous regions: 0->0
[0.480s][info][gc,metaspace] GC(0) Metaspace: 7502K(7744K)->7502K(7744K) NonClass: 6580K(6720K)->6580K(6720K) Class: 922K(1024K)->922K(1024K)
[0.480s][info][gc ] GC(0) Pause Young (Normal) (G1 Evacuation Pause) 22M->5M(254M) 3.733ms
[0.480s][info][gc,cpu ] GC(0) User=0.00s Sys=0.00s Real=0.00s
java内存区域
java内存的具体结构(注意版本的更新)
注意:是运行时的数据区域,不是编译期也不是类加载期(感觉类加载就是二进制文件翻译成可阅读的字节码文件)
1.java虚拟机栈(每个方法对应的栈帧)—线程私有
2.堆内存(new 出来的实例对象)–线程共享
3.方法区(HotSpot永久代)–线程共享
4.一些勘误更改
注意:方法区是JVM的规定叫法,永久代是Hotspot虚拟机的叫法(别的虚拟机可能根本没有永久代这个概念)
元空间使用的是直接内存,受本机可用内存的限制
对象的创建(new出来的对象都在堆中)
TLAB:Threadlocal allocate buffer
对象头的组成:3个部分Markword(hashcode,synchronized的锁升级)+指向类的指针(属于哪个类)+数组的长度
对象访问的定位方式
句柄定位:reference存放的是句柄地址(稳定)
直接指针定位:reference存放的是直接对象的地址(速度快)
string的注意点
equals()方法
注意String的hashcode()方法是重写过的,相同的字符串内容具有相同的hash码,但是内存地址不一定相同!!!
注意intern()方法是相对于String变量来说的,不然谁还调用它;没有调用的字符串变量对象创建就是在堆内存
常量折叠的优化(基本数据类型+字符串常量+final修饰的字符串变量)
注意引用不能常量折叠的优化,原因:引用的值在程序编译期是无法确定的,编译器无法对其进行优化。
final修饰的字符串常量(注意是否可以在编译期就确定,否则无法“常量折叠”的优化)
字符串拼接问题
宋红康版本–拼接讲解
intern方法(注意版本的区别)—注意和下一个问题一起理解
仅针对,构造了字符串变量时,将字符串变量调用intern()方法,针对正常的创建字符串变量还是会创建在堆中
宋红康版本的intern讲解
jdk6池中没有此字符串,将堆中的“对象的内容”赋复制一份到字符串常量池中,并返回池中的对象地址
jdk7池中没有此字符串,把堆中的“引用地址”复制一份到字符串常量池中,返回池中的这个“引用地址”
池中都有此字符串,返回字符串常量池中对象的地址
String s1 = new String(“abc”);这句话创建了几个字符串对象?
注意:new出来的对象都在堆中
垃圾回收
分代回收的具体过程
垃圾回收器的分类
注意:一般新生代回收器都是复制算法,因为存活的对象比较少,采用复制算法比较快;老年代一般采用压缩整理算法,原因可能出现新生代中大对象直接去老年代的情况,没有内存碎片问题,空间足够的大!!
吞吐量和暂停时间的关系
1.年轻代垃圾回收器
Serial
ParNew
Parallel Scavenge(适合后台运算—高吞吐量—高CPU效率)
设定一定的参数实现自适应
2.老年代CMS(Concurrent-Mark-Sweep)—(停顿时间短–适用于用户交互)
宋红康版本
不需要移动存活对象就可以避免“对象消失”的问题“”
3.G1回收器:区域化分代式(新生代+老年代)
总体特征
优势
缺点
具体过程(并发标记阶段的原始快照+region按照指针分配新的对象)
解决跨代引用问题和跨区域引用的问题(记忆集与卡表)
记忆集是一种用于记录从非收集区域指向收集区域的指针集合的抽象数据结构
解决“并发标记过程”的“对象消失”问题(CMS&G1)
类文件结构(.class文件)–非重点
总结一下:魔数+主次版本号+常量池及其数量+访问权限标志及本类父类+实现的接口及其数量+字段及其数量+方法机器数量+属性表集合
类文件加载过程(虚拟机是如何加载这些 Class 文件的)
类加载比创建对象更早,没有类对象,没办法实例化创建对象
生命周期
类加载过程
简略版本
加载阶段和连接阶段的部分内容是交叉进行的,加载阶段尚未结束,连接阶段可能就已经开始了
1.加载(3件事情)
获取二进制数据流的方式不唯一
数组类的创建
2.1链接–验证
2.2链接–准备(区别于初始化阶段的赋值)
注意:类变量,也就是静态变量,属于多个实例对象的共享对象(放在线程共享的方法区)
2.3链接–解析
将符号引用,转换为指向对象的内存地址值
3.初始化
类加载的初始化是,即构造器constructor的initial阶段;区别于对象创建的方法,程序给对象初始化
总结一下:new, static的任何相关,反射某个类的相关信息,父类未初始化,含有main方法的主类,某个实现类的接口
类的卸载(比较困难)—详情请见“类加载器”
类加载器详解(双亲委派模型)
所有的类都由“类加载器”加载,加载的作用就是将 .class文件加载到内存
类的唯一性==类本身&&类加载器
类加载器的分类
双亲委派模型
Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关可以避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。
一个自定义类的类加载器的高优先级的类加载器,类加载器之间的“父子”关系也不是通过继承来体现的,是由“优先级”来决定。
public class ClassLoaderDemo {
public static void main(String[] args) {
System.out.println("ClassLodarDemo's ClassLoader is " + ClassLoaderDemo.class.getClassLoader());
System.out.println("The Parent of ClassLodarDemo's ClassLoader is " + ClassLoaderDemo.class.getClassLoader().getParent());
System.out.println("The GrandParent of ClassLodarDemo's ClassLoader is " + ClassLoaderDemo.class.getClassLoader().getParent().getParent());
}
}
打破双亲委派模型
双亲委派的源码分析
双亲委派机制是靠ClassLoader的loadclass方法实现的