-
新生代:
复制算法. -
老年代:
清除和整理算法.
垃圾收集器分类:
Serial 串行收集器.
Parallel收集器:
其实就是Serial收集器的多线程版本.
-
ZGC & G1 & CMS
-
读写屏障
-
三色指针
-
垃圾并发和清理的过程
-
关于大对象的回收
元空间
Java8将方法区中的永久代移除了,并用元空间(metadata)代替了之前的永久代,元空间存储的是本地内存。
参数:
-Xms: 堆最小值
-Xmx: 堆最大值
-Xmn: 新生代大小
堆外内存(直接内存)
- 希望使用到缓存,但是又不想给JVM进程带来很大的压力.
- 硬盘和分布式缓存的响应时间没有堆外内存的响应快.(也就是网络IO和磁盘IO的开销).
但是堆外内存不好管控,并且要等FullGC才会回收.
from to 区域是要做预留的.
- 堆外内存默认值
堆空间最大值 -xmx:
可利用空间要减去s1的区域,因为这里会预留一部分的空间留给复制算法.
MaxDirectMemorySize = 128M
画垃圾回收过程.
CMS和G1的选择:
CMS自身会带有很多的缺点, 比如浮动垃圾,比如cur mode failed。
对于内存方面的选择适合几个G到20G左右的。
G1适合大内存的机器,起码要>8个G,然后对停顿时间又要求,小于500ms的场景。
G1的Region可以分为 Eden,survivor,old,Humongous区域.
设置了PauseTime之后,就不用动态的adapter ratio的大小了,G1会自己进行调整.
| 初始标记 | 并发标记| 重新标记 | 并发清理 |筛选回收 |
初始标记:只是做下简单的标记不会清理
并发标记,这个过程不会STW,GC线程和用户线程能够一起运行收集垃圾,所以效率很高,但也由于是并发执行的,所以这块部分也会产生垃圾,所以接下里又要借助于重新标记这个步骤标记并发过程产生的垃圾. 清理肯定是并发的很好理解.
初始标记 并发标记 最终标记 筛选回收
-
G1相对其他的垃圾收集器,更加充分的利用多核CPU的优势
-
整体采用的是标记整理的算法,当然也有部分区域的Region之间的拷贝使用复制算法。
3.没有用到清除算法,所以碎片化问题不是很明显。
-
一般G1和cms的选择点在于 8G以上的内存的服务器.
-
G1不需要借助其他垃圾收集器就能完成回收
所以对于那种存活了很长时间的对象,多次GC后还活着的对象能够起到更好的处理效果。
G1 主要是要注意pause time的设置,如果设置的过大要等到比如65的占比之后才会触发回收,那么GC过后,年轻代的存活区域超过50%(堆空间的利用率设置),很容易触发动态年龄判断机制,这样老年代很快就会被填充满.
ZGC: 了解即可,JDk11出来的。 --- 了解特点即可
1.读屏障,颜色指针,低延迟,TB级别的数据处理。
- 超大堆空间处理,超低延迟,读屏障和颜色指针。
元空间
1.8之后取消了永久代改为了元空间,并且剥离出来该空间,存放类的元信息。
优化方面
分为已经运行了一段时间的系统和未上线的系统,分为2个部分去做规划.
-
如何选择垃圾收集器?
-
4核8G,要分给操作系统2个G,分给堆只能有6个G
场景 | 选择 | 预留 |
---|---|---|
4核8G适合用G1吗? | 不适合,因为要分给操作系统2个g,最终分给堆的只有6个G,比较吃力,这个时候用CMS | - |
运行了一段时间或者上线前 | 看系统中长期规划 | - |
1.8后才推荐G1 | ||
默认是 ParallelGC | 因为双核的情况下还不如用parallel,CMS的停顿这部分优势不明显 | |
CMS还是ParallelGC? | 对于停顿时间有要求的采用CMS,如果内存大,用G1,不大用CMS,很小的话还不如用parallelGC |
[root@VM-16-13-centos ~]# java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=62082880 -XX:MaxHeapSize=993326080 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC
openjdk version "1.8.0_312"
OpenJDK Runtime Environment (build 1.8.0_312-b07)
OpenJDK 64-Bit Server VM (build 25.312-b07, mixed mode)
例子 | 内存泄漏 | OOM |
---|---|---|
Controller层写List | ✅ | |
ThreadLocal未调用remove | ✅ | |
如何定位 | jmap dump文件->mat->exclude weakreference引用,然后分析引用链,比如一个对象占用的内存特别大 | |
什么是热点代码?JIT编译器优化 | 比如for循环一块代码,Java又是解释性的语言,我对其做优化,将其变成2进制文件放在那里就可以了,相当于省去了解释执行的部分 | |
比如现在G1的吞吐量小 | 调整GC pause time,调整堆的大小比例 | |
栈上分配 | ||
堆老年代比例 | 1:2 ,老年代栈2/3 | |
参数调整 | 吞吐量和响应时间,机器数量,容量 |
比如这里的i++操作.
![](https://img.haomeiwen.com/i4464433/f9714e0a84e14a22.png)
栈上和堆分配的案例演示
@Test
public void testStackAlloc(){
DemoTest demoTest = demoTest();
demoTest.hashCode();//do something
}
String addWork(){
DemoTest demoTest = new DemoTest();
return "栈上分配,只有一个线程,直接在栈上分配,逃逸分析";
}
DemoTest demoTest(){
DemoTest demoTest = new DemoTest();
String v = "对象引用,所以会在堆上进行分配";
return demoTest;
}
图解
![](https://img.haomeiwen.com/i4464433/8a460c4c89359ed7.png)
G1将堆内存分成不同的区域然后并发的对其进行回收.
Serial串行
Parallel并行
CMS并发
G1
G1 --- 看这里
- 减少内存碎片
- 可预测的停顿时间,化整为0,可以区域化的进行扫描,不用全局扫描
- 高吞吐量
取代了CMS,没碎片,停顿时间变短,吞吐量变高.
新生代和老年代还是存在的,不过新老分成了不同的Region区域,所以角色和身份可以变化,同一块格子上次是Young,后面可能变成Old区域,减少内存的碎片,也就是我们常说的物理上不连续,逻辑(Young和Old)区上连续.
![](https://img.haomeiwen.com/i4464433/da8d509b395e2752.png)
每一小块大小是32M大小.
![](https://img.haomeiwen.com/i4464433/5b36b04d6ac4a3b2.png)
- TODO 业务场景和垃圾收集器选择
![](https://img.haomeiwen.com/i4464433/8cf5e5501b2faeae.png)
![](https://img.haomeiwen.com/i4464433/78e7c04c54f443e9.png)
- 合并E,S ,形成新的S区域,小区域合并成大区域的内存,这样合并新的空间,减少了碎片.
- 原先的不连续的空间,因为小区域的合并,空出来了位置,区域逐渐的连续了.
初始标记,并发标记,最终标记,筛选回收.
元空间 | 永久代 | |
---|---|---|
在本地内存 | 堆空间 | |
默认是21M,可以适当的调大元空间的大小 | ||
1.8将方法区(永久带)移除,用元空间代替 |
OOM分类:
- heap 堆
- direct buffer memory (直接内存,堆外内存) --- nio netty
属于操作系统管辖的范围. 1/4 大小. - metaspace(元空间)
- gc overhead limit 堆太小所导致的
- **create new native threrad **
JIT优化技术:
![](https://img.haomeiwen.com/i4464433/08177b0e390484b2.png)
同时该代码还要是热点代码.
![](https://img.haomeiwen.com/i4464433/496942648479397a.png)
- 要满足热点条件:
方法调用技术 > 1w次+
或者方法循环数超过了10700次
栈上没有垃圾回收,栈上的数据放在cpu的高速缓冲区,很快.
StringBuffer锁消除
网友评论