Jvm 参数的设置和 Jvm 调优

发布于 2024-10-18 16:59:23 字数 2749 浏览 7 评论 0

JVM 内存模型及垃圾收集算法

根据 Java 虚拟机规范,JVM 将内存划分为:

  • New(年轻代),Tenured(年老代),永久代(Perm)
  • 其中 New 和 Tenured 属于堆内存,堆内存会从 JVM 启动参数(-Xmx:3G)指定的内存中分配,Perm 不属于堆内存,有虚拟机直接分配,但可以通过-XX:PermSize -XX:MaxPermSize 等参数调整其大小
  • 年轻代(New):年轻代用来存放 JVM 刚分配的 Java 对象
  • 年老代(Tenured):年轻代中经过垃圾回收没有回收掉的对象将被 Copy 到年老代
  • 永久代(Perm):永久代存放 Class、Method 元信息,其大小跟项目的规模、类、方法的量有关,一般设置为 128M 就足够,设置原则是预留 30%的空间。

New(年轻代)又分为几个部分:

  • Eden:Eden 用来存放 JVM 刚分配的对象
  • Survivor1,Survivro2 两个 Survivor 空间一样大,当 Eden 中的对象经过垃圾回收没有被回收掉时,会在两个 Survivor 之间来回 Copy,当满足某个条件,比如 Copy 次数,就会被 Copy 到 Tenured(年老代)。显然,Survivor 只是增加了对象在年轻代中的逗留时间,增加了被垃圾回收的可能性。

垃圾回收算法

  • 垃圾回收算法可以分为三类,都基于标记-清除(复制)算法:
  • Serial 算法(单线程) 并行算法 并发算法 JVM 会根据机器的硬件配置对每个内存代选择适合的回收算法,比如,如果机器多于 1 个核,会对年轻代选择并行算法
  • 并行算法是用多线程进行垃圾回收,回收期间会暂停程序的执行,而并发算法,也是多线程回收,但期间不停止应用执行。所以,并发算法适用于交互性高的一些程序。经过观察,并发算法会减少年轻代的大小,其实就是使用了一个大的年老代,这反过来跟并行算法相比吞吐量相对较低。

垃圾回收动作何时执行?

  • 当年轻代内存满时,会引发一次普通 GC,该 GC 仅回收年轻代。需要强调的时,年轻代满是指 Eden 代满,Survivor 满不会引发 GC
  • 当年老代满时会引发 Full GC,Full GC 将会同时回收年轻代、年老代
  • 当永久代满时也会引发 Full GC,会导致 Class、Method 元信息的卸载

何时会抛出 OutOfMemoryException

  • 并不是内存被耗空的时候才抛出,JVM98%的时间都花费在内存回收 每次回收的内存小于 2% 满足这两个条件将触发 OutOfMemoryException
  • 这将会留给系统一个微小的间隙以做一些 Down 之前的操作,比如手动打印 Heap Dump。

内存泄漏及解决方法

系统崩溃前的一些现象

  • 每次垃圾回收的时间越来越长,由之前的 10ms 延长到 50ms 左右,FullGC 的时间也有之前的 0.5s 延长到 4、5s
  • FullGC 的次数越来越多,最频繁时隔不到 1 分钟就进行一次 FullGC
  • 年老代的内存越来越大并且每次 FullGC 后年老代没有内存被释放
  • 之后系统会无法响应新的请求,逐渐到达 OutOfMemoryError 的临界值。

生成堆的 dump 文件

通过 JMX 的 MBean 生成当前的 Heap 信息,大小为一个 3G(整个堆的大小)的 hprof 文件,如果没有启动 JMX 可以通过 Java 的 jmap 命令来生成该文件。

列出当前所有运行的 java 进程:jcmd -l
列出当前运行的 java 进程可以执行的操作:jcmd PID help
#生成 dump
jmap -dump:live,format=b,file=e:/heap.hprof 10444

分析 dump 文件

下面要考虑的是如何打开这个 3G 的堆信息文件,显然一般的 Window 系统没有这么大的内存,必须借助高配置的 Linux。当然我们可以借助 X-Window 把 Linux 上的图形导入到 Window。我们考虑用下面几种工具打开该文件:

Visual VM
IBM HeapAnalyzer
JDK 自带的 Hprof 工具

使用这些工具时为了确保加载速度,建议设置最大内存为 6G。使用后发现,这些工具都无法直观地观察到内存泄漏,Visual VM 虽能观察到对象大小,但看不到调用堆栈;HeapAnalyzer 虽然能看到调用堆栈,却无法正确打开一个 3G 的文件。因此,我们又选用了 Eclipse 专门的静态内存分析工具:Mat。 ​

为什么崩溃前垃圾回收的时间越来越长?

根据内存模型和垃圾回收算法,垃圾回收分两部分:内存标记、清除(复制),标记部分只要内存大小固定时间是不变的,变的是复制部分,因为每次垃圾回收都有一些回收不掉的内存,所以增加了复制量,导致时间延长。所以,垃圾回收的时间也可以作为判断内存泄漏的依据

为什么 Full GC 的次数越来越多?

因此内存的积累,逐渐耗尽了年老代的内存,导致新对象分配没有更多的空间,从而导致频繁的垃圾回收

为什么年老代占用的内存越来越大?

因为年轻代的内存无法被回收,越来越多地被 Copy 到年老代。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

笔芯

暂无简介

0 文章
0 评论
24 人气
更多

推荐作者

lee_heart

文章 0 评论 0

往事如风

文章 0 评论 0

春风十里

文章 0 评论 0

纸短情长

文章 0 评论 0

qq_pdEUFz

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文