p39
- 老年代是jdk8之前的叫法,jdk8之后叫元空间。
-
堆和方法区是jvm全局共有,程序计数器、本地方法栈、虚拟机栈都是每个线程一份
示意图
阿里版本的架构划分
p43

p44
栈是运行时单位,而堆是存储的单位

p45
栈- stackoverflowError
堆-oom- outOfMemoryError
-Xss jvm 栈大小设置的参数
p47
动态连接、方法返回地址和一些附加信息统称为帧数据区

48
局部变量表:定义为一个数字数组,主要用于存储方法参数和定义在方法体内的局部变量;局部变量是建立在线程的栈上,是线程的私有数据,不存在线程安全问题
局部变量的单位是槽(slot),double和long占据两个槽位,局部变量出了作用域,他所在的局部变量表的槽位会被其他变量所占据
操作数栈:主要用于保存计算过程的中间结果,同事作为计算过程中的变量的临时储存空间
p54
栈顶缓存技术:将栈顶元素全部缓存在物理cpu的寄存器中,降低对内存的读/写次数,提升执行引擎的执行效率
p55 动态链接(也叫指向运行时常量池的方法引用)


p56-57 方法的调用
静态连接-早期绑定-非虚方法
动态连接-晚期绑定
例子:
多态,方法传参是父类,编译期间无法确定是哪个子类所以是晚期绑定
重载?构造方法?是早期绑定
静态方法,私有方法,final方法,构造方法,服了方法都是非虚方法



p60 方法返回地址

p62 栈的面试题
堆溢出:不断的new 一个对象,一直创建新的对象
栈溢出:死循环或者是递归太深,递归的原因,可能太大,也可能没有终止。(调用方法过多)
分配的栈内存越大越好吗
jvm整个空间是有限的,如果栈的内存过大,jvm所能创建的线程数量就过少了
P66堆

P67堆、栈和GC的关系
如果代码中new了一个对象,首先虚机栈的栈针会指向当前方法,堆会给该对象分配空间,虚机栈的操作数栈会引用堆中的该对象的引用地址。当当前方法执行完毕后,栈针出栈,栈和堆的引用会消失。没有被引用的堆空间,等到某个时刻进行GC释放。
如果频繁GC,Gc线程活跃,用户线程受阻,程序效率变慢
p68 堆的结构


P69 设置堆空间大小

-X是jvm的运行参数
ms是memory start
mx是memory max
如何查看进程的堆大小
方式一:jps /jstat -gc 进程id
方式二:-XX:+PrintGCDetails
p71

-XX:NewRatio 参数一般不调整,默认是1:2


YGC/Minor GC:当伊甸园区满了时候会发生YGC
幸存者0区满了之后,忽略阈值15,直接放到老年代

79 内存分配策略

TLAB(Thread Local Allocation Buffer)


P81

逃逸分析(用个人的话定义)
如果一个参数存在于两个方法,叫做逃逸。jvm在分配内存时候会进行逃逸分析,假如参数没有发生逃逸会直接分配到该方法的栈帧上,会随着栈帧出栈。这样做的好处减少gc频率,在堆内存不足时候降低oom的概率。

标量替换(用个人的话定义)
基本数据类型为标量;含有多个属性的对象称之为聚合量
未发生逃逸的聚合量会被替换成标量,叫做标量替换。因为没有逃逸所以也会栈上分配
87栈、堆和方法区关系

方法区在逻辑上是堆的一部分,但是实现方法不一样,方法还有个别名:非堆(Non-heap);

元空间不在jvm里在本地物理机的内存中
方法区的大小可以自动扩容和收缩,也可以设置固定大小


92方法区的结构





p97
方法区演进细节
| jdk版本 | 变化 |
|---|---|
| jdk1.6之前 | 有永久代,静态变量存在永久代 |
| jdk1.7 | 有永久代,但逐步在去除永久代,字符串常量池、静态变量移除放在堆里 |
| jdk1.8 | 无永久代,类型信息、字段、方法常量保存在本地内存的元空间,但字符串常量池、静态变量仍在堆里 |
永久代被元空间取代的原因
- 为永久代设置空间大小是很难确定的
- 对永久代进行调优是很困难的

p100方法区的垃圾回收


p105对象内存布局

p106对象访问定位
句柄访问:虚机栈-栈帧-本地变量表 引用堆中句柄池中对应地址,该对应地址存储这堆中实际对象地址以及方法区中类的信息地址
直接访问::虚机栈-栈帧-本地变量表 引用堆中对象地址,对象地址指向方法区中类的信息地址
句柄访问缺点:需要专门开辟句柄池,更消耗空间,到堆中对象需要两次引用,性能较差。
优点:当堆中对象地址移动的时候,本地变量表中地址不需要改变,稳定解耦
p106直接内存
直接内存就是物理机系统内存,调用直接内存会比调用堆中数据会更快一点,缺点:分配回收成本高;不受jvm内存回收管理
可以由MaxDirectMemorySize设置直接内存大小,如果没设置,默认和堆最大内存一致
java代码申请内存小例子

P110执行引擎
执行引擎负责把高级语言(字节码)翻译成机器语言

p115jit和热点代码

新名词:方法调用计数器、回边计数器


其中代码缓存存在方法区


64位默认是server模式




P119
JDK8中String底层是char[]
JDK9中String底层是byte[]+编码标记
原因

P120

p123-字符串变量拼接操作的底层原理
String a1="1"; //存储在常量池
String a2= a1+"2"; //因为有变量运算,所以存储在堆里
String a0= a1.intern()+"2" //intern返回的对象也在常量池
final String a3 = "1";
final String a4 = "2";
String a5 = a3 + a4; //final修饰的的变量时确定的 所以a5也在常量池,且存在编译优化,直接等于String a5="12"
/**
*String的”+“操作相当于
*StringBuilder s =new StringBuilder();
*s.append();
*s.append();
*return s.toString();
**/
String a5 = a1 + a2;
Q:既然String的+操作能自动优化成StringBuilder为什么还建议字符串累加使用StringBuffer效率更高


A:1.频繁创建对象2.创建的对象也需要回收
Q:StringBuilder的进一步优化空间
A:StringBuilder sb=new StringBuilder(xxx);设置底层数组的初始大小,避免数组频繁扩容

重点:intern在jdk6版本时new一个新对象放在常量池,在jdk7以后的版本是用旧字符地址放在常量池当中
StingBuilder sb=new StringBuilder("ab").toString(),实际底层toString()也创建一个都对象(等价于new Sring(char[]),注意构造方法的入参为char[]时候,不会加入常量池
p131
对于大型网站,使用intern函数,把字符串存储在常量池中,有三点好处
1.空间效率上更节省
2.代码效率也更快
3.gc不会那么频繁
p139引用计数算法


p141可达性分析算法(根搜索算法、追踪性垃圾回收)


对象在销毁时候会调用finalize()方法

可达性分析的两次标记

p147标记清除算法


p148复制算法

复制算法是为了解决标记清除算法容易产生内存碎片的问题,特别的,如果垃圾对象特别多,存活对象较少,复制算法效率会更快
p148标记压缩算法

P150

P151分代收集算法
年轻代--复制算法
老年代--标记清除 和标记整理混用
P152增量收集算法


P153分区算法的说明

p158内存泄漏
静态变量,单例模式,未释放的jdbc链接都是内存泄漏的例子
p162安全点 安全域


安全区域:线程处于sleep或者阻塞状态
P163强引用、弱引用,软引用、虚引用
强引用:永远不会回收,也是内存泄露的主要原因。
软引用:在垃圾回收后内存空间依然不足就会回收软引用对象。用途:可大可小的缓存。
弱引用:发现即回收,垃圾回收时候弱引用正常被回收。场景:可有可无的缓存(知识点:weakHashMap)
虚引用:不能通过虚引用获取到实例,场景:跟踪垃圾回收时间
P170垃圾回收器的分类
| 分类方式 | A | B |
|---|---|---|
| 线程数 | 串行垃圾回收 | 并行垃圾回收 |
| 工作模式 | 并发式垃圾回收 | 独占式垃圾回收 |
| 碎片的处理方式 | 压缩式垃圾回收 | 非压缩式垃圾回收 |
| 工作的内存空间 | 年轻代垃圾回收 | 老年代垃圾回收 |
P171 GC性能指标

吞吐量低:垃圾回收次数少,单次回收时间长
低延迟:垃圾回收次数长,单次回收时间长短,总会收时间长。回收线程和用户线程来回切换,cpu开销时间长

P174



并行:多个垃圾回收线程
并发:用户线程和垃圾回收线程并发


java的web应用程序一般不会用serial,一般用于桌面程序
178ParNew
除了是并行的和serial没有什么区别


P180 ParallelScavenge(并行 吞吐量优先)

参数:-XX:MaxGCPauseMillis 设置垃圾回收最大停顿时间,为了实现该参数,jvm会自动修改java堆参数或其他参数,该参数较小的话,回收频率会增高,慎用该参数



P182CMS(consurrent-Mark-Sweep 并发 低延迟 )


参数


P186

并行和并发、分代收集、空间整合、可预测的停顿时间模型




P195垃圾回收器的总结


