JVM 性能调优

在高性能硬件上部署程序,目前次要有两种形式:

  • 通过 64 位 JDK 来应用大内存;
  • 应用若干个 32 位虚拟机建设逻辑集群来利用硬件资源。

应用 64 位 JDK 治理大内存

堆内存变大后,尽管垃圾收集的频率缩小了,但每次垃圾回收的工夫变长。 如果堆内存为 14 G,那么每次 Full GC 将长达数十秒。如果 Full GC 频繁产生,那么对于一个网站来说是无法忍受的。

对于用户交互性强、对进展工夫敏感的零碎,能够给 Java 虚拟机调配超大堆的前提是有把握把应用程序的 Full GC 频率管制得足够低,至多要低到不会影响用户应用。

可能面临的问题:

  • 内存回收导致的长时间进展;
  • 现阶段,64 位 JDK 的性能广泛比 32 位 JDK 低;
  • 须要保障程序足够稳固,因为这种利用要是产生堆溢出简直就无奈产生堆转储快照(因为要产生超过 10GB 的 Dump 文件),哪怕产生了快照也简直无奈进行剖析;
  • 雷同程序在 64 位 JDK 耗费的内存个别比 32 位 JDK 大,这是因为指针收缩,以及数据类型对齐补白等因素导致的。

应用 32 位 JVM 建设逻辑集群

在一台物理机器上启动多个应用服务器过程,每个服务器过程调配不同端口, 而后在前端搭建一个负载均衡器,以反向代理的形式来调配拜访申请。

思考到在一台物理机器上建设逻辑集群的目标仅仅是为了尽可能利用硬件资源,并不需要关怀状态保留、热转移之类的高可用性能需要, 也不须要保障每个虚拟机过程有相对的平衡负载,因而应用无 Session 复制的亲合式集群是一个不错的抉择。 咱们仅仅须要保障集群具备亲合性,也就是均衡器按肯定的规定算法(个别依据 SessionID 调配) 将一个固定的用户申请永远调配到固定的一个集群节点进行解决即可。

可能遇到的问题:

  • 尽量避免节点竞争全局资源,如磁盘竞争,各个节点如果同时拜访某个磁盘文件的话,很可能导致 IO 异样;
  • 很难高效利用资源池,如连接池,个别都是在节点建设本人独立的连接池,这样有可能导致一些节点池满了而另外一些节点仍有较多空余;
  • 各个节点受到 32 位的内存限度;
  • 大量应用本地缓存的利用,在逻辑集群中会造成较大的内存节约,因为每个逻辑节点都有一份缓存,这时候能够思考把本地缓存改成集中式缓存。

调优案例剖析与实战

场景形容

一个小型零碎,应用 32 位 JDK,4G 内存,测试期间发现服务端不定时抛出内存溢出异样。 退出 -XX:+HeapDumpOnOutOfMemoryError(增加这个参数后,堆内存溢出时就会输入异样日志), 但再次发生内存溢出时,没有生成相干异样日志。

剖析

在 32 位 JDK 上,1.6G 调配给堆,还有一部分调配给 JVM 的其余内存,间接内存最大也只能在残余的 0.4G 空间中分出一部分, 如果应用了 NIO,JVM 会在 JVM 内存之外分配内存空间,那么就要小心“间接内存”有余时产生内存溢出异样了。

间接内存的回收过程

间接内存尽管不是 JVM 内存空间,但它的垃圾回收也由 JVM 负责。

垃圾收集进行时,虚拟机尽管会对间接内存进行回收, 然而间接内存却不能像新生代、老年代那样,发现空间有余了就告诉收集器进行垃圾回收, 它只能等老年代满了后 Full GC,而后“顺便”帮它清理掉内存的废除对象。 否则只能始终等到抛出内存溢出异样时,先 catch 掉,再在 catch 块里大喊 “System.gc()”。 要是虚拟机还是不听,那就只能眼睁睁看着堆中还有许多闲暇内存,本人却不得不抛出内存溢出异样了。

JVM 性能调优

在高性能硬件上部署程序,目前次要有两种形式:

  • 通过 64 位 JDK 来应用大内存;
  • 应用若干个 32 位虚拟机建设逻辑集群来利用硬件资源。

应用 64 位 JDK 治理大内存

堆内存变大后,尽管垃圾收集的频率缩小了,但每次垃圾回收的工夫变长。 如果堆内存为 14 G,那么每次 Full GC 将长达数十秒。如果 Full GC 频繁产生,那么对于一个网站来说是无法忍受的。

对于用户交互性强、对进展工夫敏感的零碎,能够给 Java 虚拟机调配超大堆的前提是有把握把应用程序的 Full GC 频率管制得足够低,至多要低到不会影响用户应用。

可能面临的问题:

  • 内存回收导致的长时间进展;
  • 现阶段,64 位 JDK 的性能广泛比 32 位 JDK 低;
  • 须要保障程序足够稳固,因为这种利用要是产生堆溢出简直就无奈产生堆转储快照(因为要产生超过 10GB 的 Dump 文件),哪怕产生了快照也简直无奈进行剖析;
  • 雷同程序在 64 位 JDK 耗费的内存个别比 32 位 JDK 大,这是因为指针收缩,以及数据类型对齐补白等因素导致的。

应用 32 位 JVM 建设逻辑集群

在一台物理机器上启动多个应用服务器过程,每个服务器过程调配不同端口, 而后在前端搭建一个负载均衡器,以反向代理的形式来调配拜访申请。

思考到在一台物理机器上建设逻辑集群的目标仅仅是为了尽可能利用硬件资源,并不需要关怀状态保留、热转移之类的高可用性能需要, 也不须要保障每个虚拟机过程有相对的平衡负载,因而应用无 Session 复制的亲合式集群是一个不错的抉择。 咱们仅仅须要保障集群具备亲合性,也就是均衡器按肯定的规定算法(个别依据 SessionID 调配) 将一个固定的用户申请永远调配到固定的一个集群节点进行解决即可。

可能遇到的问题:

  • 尽量避免节点竞争全局资源,如磁盘竞争,各个节点如果同时拜访某个磁盘文件的话,很可能导致 IO 异样;
  • 很难高效利用资源池,如连接池,个别都是在节点建设本人独立的连接池,这样有可能导致一些节点池满了而另外一些节点仍有较多空余;
  • 各个节点受到 32 位的内存限度;
  • 大量应用本地缓存的利用,在逻辑集群中会造成较大的内存节约,因为每个逻辑节点都有一份缓存,这时候能够思考把本地缓存改成集中式缓存。

调优案例剖析与实战

场景形容

一个小型零碎,应用 32 位 JDK,4G 内存,测试期间发现服务端不定时抛出内存溢出异样。 退出 -XX:+HeapDumpOnOutOfMemoryError(增加这个参数后,堆内存溢出时就会输入异样日志), 但再次发生内存溢出时,没有生成相干异样日志。

剖析

在 32 位 JDK 上,1.6G 调配给堆,还有一部分调配给 JVM 的其余内存,间接内存最大也只能在残余的 0.4G 空间中分出一部分, 如果应用了 NIO,JVM 会在 JVM 内存之外分配内存空间,那么就要小心“间接内存”有余时产生内存溢出异样了。

间接内存的回收过程

间接内存尽管不是 JVM 内存空间,但它的垃圾回收也由 JVM 负责。

垃圾收集进行时,虚拟机尽管会对间接内存进行回收, 然而间接内存却不能像新生代、老年代那样,发现空间有余了就告诉收集器进行垃圾回收, 它只能等老年代满了后 Full GC,而后“顺便”帮它清理掉内存的废除对象。 否则只能始终等到抛出内存溢出异样时,先 catch 掉,再在 catch 块里大喊 “System.gc()”。 要是虚拟机还是不听,那就只能眼睁睁看着堆中还有许多闲暇内存,本人却不得不抛出内存溢出异样了。

本文由mdnice多平台公布