如何在应用程序中保持5GB持续内存而不会因GC导致性能不佳?

Avi*_*ano 9 java performance garbage-collection

我的应用程序是地理应用程序.由于需要较短的响应时间,我的每个实例都会将所有点加载到内存中并将它们存储在结构中(四叉树).

每分钟我们加载所有点(与db同步)并将它们放在几个四叉树中.

我们现在有0.5GB积分.我正在努力准备下一级5GB积分.JVM:-XX:NewSize = 6g -Xms20g -Xmx20g -XX:+ UseConcMarkSweepGC -verboseGC -XX:+ PrintGCTimeStamps -XX:+ PrintGCDateStamps -XX:+ PrintGCDetails

实例的启动花费了很多次,因为GC在额外的应用程序中一直受到GC的影响.

我想对大堆的GC有一些参考.

我可以想到几个解决方案:

  1. 仅刷新数据库中的更改,而不是每次都加载所有数据库.缺点 - 在应用的早期阶段仍然会遭受GC,长时间GC.

  2. 关堆解决方案.在四元树中存储点ids并将点存储在堆外.缺点 - 序列化时间,地理结构是少数对象的复杂而不是简单的对象.

  3. 对于每个实例,使用针对此实例的结构和查询创建其他实例.geo实例将保存长寿命对象,并且可以对长寿命对象进行GC调整.缺点 - 复杂性和响应时间.

对于持有少量GIG长寿命对象的应用程序的文章的任何引用都将受到欢迎.

在Ubuntu(亚马逊)上运行.Java 7.Dosnt有内存限制.

问题是每次刷新数据时都会有很长的暂停时间. GC

用于刷新的Gc日志:

2014-06-15T16:32:58.551+0000: 1037.469: [GC2014-06-15T16:32:58.551+0000: 1037.469: [ParNew: 5325855K->259203K(5662336K), 0.0549830 secs] 16711893K->11645244K(20342400K), 0.0551490 secs] [Times: user=0.71 sys=0.00, real=0.05 secs] 

2014-06-15T16:33:02.383+0000: 1041.302: [GC2014-06-15T16:33:02.383+0000: 1041.302: [ParNew: 5292419K->470768K(5662336K), 0.0851740 secs] 16678460K->11856811K(20342400K), 0.0853260 secs] [Times: user=1.09 sys=0.00, real=0.09 secs] 

2014-06-15T16:33:06.114+0000: 1045.033: [GC2014-06-15T16:33:06.114+0000: 1045.033: [ParNew: 5503984K->629120K(5662336K), 1.5475170 secs] 16890027K->12193877K(20342400K), 1.5476760 secs] [Times: user=5.49 sys=0.61, real=1.55 secs] 

2014-06-15T16:33:11.145+0000: 1050.063: [GC2014-06-15T16:33:11.145+0000: 1050.063: [ParNew: 5662336K->558612K(5662336K), 0.7742870 secs] 17227093K->12758866K(20342400K), 0.7744610 secs] [Times: user=3.88 sys=0.82, real=0.77 secs] 

2014-06-15T16:33:11.920+0000: 1050.838: [GC [1 CMS-initial-mark: 12200254K(14680064K)] 12761216K(20342400K), 0.1407080 secs] [Times: user=0.13 sys=0.01, real=0.14 secs] 

2014-06-15T16:33:12.061+0000: 1050.979: [CMS-concurrent-mark-start]

2014-06-15T16:33:14.208+0000: 1053.127: [CMS-concurrent-mark: 2.148/2.148 secs] [Times: user=19.46 sys=0.44, real=2.15 secs] 

2014-06-15T16:33:14.208+0000: 1053.127: [CMS-concurrent-preclean-start]

2014-06-15T16:33:14.232+0000: 1053.150: [CMS-concurrent-preclean: 0.023/0.023 secs] [Times: user=0.14 sys=0.01, real=0.02 secs] 

2014-06-15T16:33:14.232+0000: 1053.150: [CMS-concurrent-abortable-preclean-start]

2014-06-15T16:33:15.629+0000: 1054.548: [GC2014-06-15T16:33:15.630+0000: 1054.548: [ParNew: 5591828K->563654K(5662336K), 0.1279360 secs] 17792082K->12763908K(20342400K), 0.1280840 secs] [Times: user=1.65 sys=0.00, real=0.13 secs] 

2014-06-15T16:33:19.143+0000: 1058.062: [GC2014-06-15T16:33:19.143+0000: 1058.062: [ParNew: 5596870K->596692K(5662336K), 0.3445070 secs] 17797124K->13077191K(20342400K), 0.3446730 secs] [Times: user=3.06 sys=0.34, real=0.35 secs] 

 CMS: abort preclean due to time 2014-06-15T16:33:19.832+0000: 1058.750: [CMS-concurrent-abortable-preclean: 5.124/5.600 secs] [Times: user=35.91 sys=1.67, real=5.60 secs] 
Run Code Online (Sandbox Code Playgroud)

Avi*_*ano 0

我尝试使用新的大小、堆大小来调整 JVM 运行...还尝试了 G1 (-XX:MaxGCPauseMillis=200),但没有成功,并且很快就出现了 OOM。

最终的解决方案是仅加载更改。当负载仅发生变化时,GC 暂停时间仅为 0.55%,平均暂停时间为 17.3ms。

加载所有数据时,您不会遇到并发问题,因为您不需要在更新时锁定服务类结构(四元组、映射),因为您创建了一个新的服务类,并且一旦新的服务类准备好为您提供更新服务服务类与更新类的参考。为了仍然避免锁定服务数据,我们基于旧数据创建一个新的服务类,并将更新后的数据合并到其中,因此之前加载并存在于老一代中的 99.9% 的数据将不需要被删除。干净的 。gc 将仅清理四叉树引用和映射键引用,并将由 CMS 清理。

如果您必须完全刷新数据,请不要一次性执行此操作。尝试将数据分解为少数结构,并偶尔刷新每个数据结构,在这种情况下,GC 将遭受更少的暂停时间和昂贵的故障。

如果您必须一次性执行此操作并且您可以承受长时间的暂停,而不是创建一个小的 newsize ,那么该对象将传递给旧一代。

在我们的测试中,有 6 个线程的恒定负载,每 3 分钟我们加载 3.5G 的服务数据并替换旧的数据。使用以下命令完成的测试:-XX:NewSize=6g -Xms20g -Xmx20g -XX:+UseConcMarkSweepGC

屈服: GC 6G 新尺寸

GC 发生的频率较低(平均收集间隔 (ms) 3735),但大多数需要 4 秒左右。

当更改为:-XX:CMSInitiatingOccupancyFraction=70 -Xms20g -Xmx20g -XX:+UseConcMarkSweepGC

屈服: 新尺寸小 GC