概述
JAVA提供了垃圾回收器(garbage collector)来自动检测对象的作用域),可自动把不再被使用的存储空间释放掉,也就是说,GC机制可以有效地防止内存泄露以及内存溢出。
GC任务
JAVA 垃圾回收器的主要任务是:
- 分配内存
- 确保被引用对象的内存不被错误地回收
- 回收不再被引用的对象的内存空间
凡事都有两面性。垃圾回收器在把程序员从释放内存的复杂工作中解放出来的同时,为了实现垃圾回收,garbage collector必须跟踪内存的使用情况,释放没用的对象,在完成内存的释放之后还需要处理堆中的碎片, 这样做必定会增加JVM的负担。
JAVA 内存区域
先看看JAVA程序在执行的时候,内存究竟是如何划分的。
![27dd1a2c5c6189abb0b099150a02e731.png](https://i-blog.csdnimg.cn/blog_migrate/e61d17e16ee4e9a74c01067e9fe40ef6.jpeg)
私有内存区的区域名称和相应的特性如下表所示:
![9fff24da71c283fa0339fa0e610579cf.png](https://i-blog.csdnimg.cn/blog_migrate/6aeb490d0cbf5eb5972310a330e72eff.jpeg)
这里主要说一下虚拟机栈中的局部变量表,里面存放了三个信息:
- 各种基本数据类型(boolean、byte、char、short、int、float、long、double)
- 对象引用(reference)
- returnAddress地址
这个returnAddress和程序计数器有什么区别?前者是指示JVM的指令执行到哪一行,后者则是代码执行到哪一行。
私有内存区伴随着线程的产生而产生,一旦线程中止,私有内存区也会自动消除。
![f1ab3d15038216b83e97f8c8c0637427.png](https://i-blog.csdnimg.cn/blog_migrate/878eb815c0eec6f7478a8734334e3c87.jpeg)
JAVA堆
堆内存是由存活和死亡的对象组成的。存活的对象是应用可以访问的,不会被垃圾回收。死亡的对象是应用不可访问尚且还没有被垃圾收集器回收掉的对象。一直到垃圾收集器把这些对象回收掉之前,他们会一直占据堆内存空间。
堆是应用程序在运行期请求操作系统分配给自己的向高地址扩展的数据结构,是不连续的内存区域。用一句话总结堆的作用:程序运行时动态申请某个大小的内存空间。
![5116446b1363b5efbaa938e65ab56d6f.png](https://i-blog.csdnimg.cn/blog_migrate/55293f1efe7e30fe6375c9137fcfcb93.jpeg)
新生代:刚刚新建的对象在Eden中,经历一次Minor GC,Eden中的存活对象就会被移动到第一块survivor space S0,Eden被清空;等Eden区再满了,就再触发一次Minor GC,Eden和S0中的存活对象又会被复制送入第二块survivor space S1。S0和Eden被清空,然后下一轮S0与S1交换角色,如此循环往复。如果对象的复制次数达到16次,该对象就会被送到老年代中。
老年代:如果某个对象经历了几次垃圾回收之后还存活,就会被存放到老年代中。老年代的空间一般比新生代大。
![c90272c2aa370b3296b0f2dbc4737338.png](https://i-blog.csdnimg.cn/blog_migrate/9600eceb588e03f9bc626a1b674d8e0e.jpeg)
不过实际运行中,Major GC会伴随至少一次 Minor GC,因此也不必过多纠结于到底是哪种GC(在有些资料中看到把full GC和Minor GC等价的说法)。
那么,当我们创建一个对象后,它会被放在堆内存的哪个部分呢?
![2246af5cdf7d08c3f6b0e8567ca4a6ad.png](https://i-blog.csdnimg.cn/blog_migrate/d0cfdcf327bdbcce6741141bf7eb0a4d.jpeg)
如果Major GC之后还是老年代不足,那JVM就会抛出内存不足的异常。
JVM优化参数
1. 与串行回收器相关的参数
-XX:+UseSerialGC:在新生代和老年代使用串行回收器。
-XX:+SuivivorRatio:设置 eden 区大小和 survivor 区大小的比例。
-XX:+PretenureSizeThreshold:设置大对象直接进入老年代的阈值。当对象的大小超过这个值时,将直接在老年代分配。
-XX:MaxTenuringThreshold:设置对象进入老年代的年龄的最大值。每一次 Minor GC 后,对象年龄就加 1。任何大于这个年龄的对象,一定会进入老年代。
2. 与并行 GC 相关的参数
-XX:+UseParNewGC: 在新生代使用并行收集器。
-XX:+UseParallelOldGC: 老年代使用并行回收收集器。
-XX:ParallelGCThreads:设置用于垃圾回收的线程数。通常情况下可以和 CPU 数量相等。但在 CPU 数量比较多的情况下,设置相对较小的数值也是合理的。
-XX:MaxGCPauseMills:设置最大垃圾收集停顿时间。它的值是一个大于 0 的整数。收集器在工作时,会调整 Java 堆大小或者其他一些参数,尽可能地把停顿时间控制在 MaxGCPauseMills 以内。
-XX:GCTimeRatio:设置吞吐量大小,它的值是一个 0-100 之间的整数。假设 GCTimeRatio 的值为 n,那么系统将花费不超过 1/(1+n) 的时间用于垃圾收集。
-XX:+UseAdaptiveSizePolicy:打开自适应 GC 策略。在这种模式下,新生代的大小,eden 和 survivor 的比例、晋升老年代的对象年龄等参数会被自动调整,以达到在堆大小、吞吐量和停顿时间之间的平衡点。
3. 与 CMS 回收器相关的参数
-XX:+UseConcMarkSweepGC: 新生代使用并行收集器,老年代使用 CMS+串行收集器。
-XX:+ParallelCMSThreads: 设定 CMS 的线程数量。
-XX:+CMSInitiatingOccupancyFraction:设置 CMS 收集器在老年代空间被使用多少后触发,默认为 68%。
-XX:+UseFullGCsBeforeCompaction:设定进行多少次 CMS 垃圾回收后,进行一次内存压缩。
-XX:+CMSClassUnloadingEnabled:允许对类元数据进行回收。
-XX:+CMSParallelRemarkEndable:启用并行重标记。
-XX:CMSInitatingPermOccupancyFraction:当永久区占用率达到这一百分比后,启动 CMS 回收 (前提是-XX:+CMSClassUnloadingEnabled 激活了)。
-XX:UseCMSInitatingOccupancyOnly:表示只在到达阈值的时候,才进行 CMS 回收。
-XX:+CMSIncrementalMode:使用增量模式,比较适合单 CPU。
4. 与 G1 回收器相关的参数
-XX:+UseG1GC:使用 G1 回收器。
-XX:+UnlockExperimentalVMOptions:允许使用实验性参数。
-XX:+MaxGCPauseMills:设置最大垃圾收集停顿时间。
-XX:+GCPauseIntervalMills:设置停顿间隔时间。
5. 其他参数
-XX:+DisableExplicitGC: 禁用显示 GC。
常用参数如下
![f52b07259ee429b4acccbc1ad7be4237.png](https://i-blog.csdnimg.cn/blog_migrate/91711737e6eb98d0d61155ec8ee1ff90.jpeg)
后面会分享更多devops和DBA方面的内容,感兴趣的朋友可以关注一下~
![fc66e777f0f234325c7294c3b70fbf2f.gif](https://i-blog.csdnimg.cn/blog_migrate/58c0ca34c087fdbbe53baf38e39cb8f1.gif)