我在为异步消息传递开发轻量级库的过程中遇到了这种情况。为了了解创建大量中等大小的短生命周期对象的成本,我编写了下面的测试:
import java.nio.ByteBuffer;
import java.util.Random;
public class MemPressureTest {
static final int SIZE = 4096;
static final class Bigish {
final ByteBuffer b;
public Bigish() {
this(ByteBuffer.allocate(SIZE));
}
public Bigish(ByteBuffer b) {
this.b = b;
}
public void fill(byte bt) {
b.clear();
for (int i = 0; i < SIZE; ++i) {
b.put(bt);
}
}
}
public static void main(String[] args) {
Random random = new Random(1);
Bigish tmp = new Bigish();
for (int i = 0; i < 3e7; ++i) {
tmp.fill((byte)random.nextInt(255));
}
}
}
在我的笔记本电脑上,使用默认的GC设置,它运行大约需要95秒:
/tmp$ time java -Xlog:gc MemPressureTest
[0.006s][info][gc] Using G1
real 1m35.552s
user 1m33.658s
sys 0m0.428s
这就是事情变得奇怪的地方。我调整了程序,为每次迭代分配一个新对象:
...
Random random = new Random(1);
for (int i = 0; i < 3e7; ++i) {
Bigish tmp = new Bigish();
tmp.fill((byte)random.nextInt(255));
}
...
理论上,这应该会增加一些小开销,但是没有一个对象应该被提升到Eden之外。充其量,我希望运行时接近相同。然而,这个测试在大约17秒内完成:
/tmp$ time java -Xlog:gc MemPressureTest
[0.007s][info][gc] Using G1
[0.090s][info][gc] GC(0) Pause Young (Normal) (G1 Evacuation Pause) 23M->1M(130M) 1.304ms
[0.181s][info][gc] GC(1) Pause Young (Normal) (G1 Evacuation Pause) 76M->1M(130M) 0.870ms
[0.247s][info][gc] GC(2) Pause Young (Normal) (G1 Evacuation Pause) 76M->0M(130M) 0.844ms
[0.317s][info][gc] GC(3) Pause Young (Normal) (G1 Evacuation Pause) 75M->0M(130M) 0.793ms
[0.381s][info][gc] GC(4) Pause Young (Normal) (G1 Evacuation Pause) 75M->0M(130M) 0.859ms
[lots of similar GC pauses, snipped for brevity]
[16.608s][info][gc] GC(482) Pause Young (Normal) (G1 Evacuation Pause) 254M->0M(425M) 0.765ms
[16.643s][info][gc] GC(483) Pause Young (Normal) (G1 Evacuation Pause) 254M->0M(425M) 0.580ms
[16.676s][info][gc] GC(484) Pause Young (Normal) (G1 Evacuation Pause) 254M->0M(425M) 0.841ms
real 0m16.766s
user 0m16.578s
sys 0m0.576s
我运行了两个版本几次,结果与上述结果几乎相同。我觉得我一定错过了一些非常明显的东西。我疯了吗?什么可以解释这种性能差异?
===编辑===
按照阿潘金和丹1st的建议,我用JMH重写了测试:
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.infra.Blackhole;
import java.nio.ByteBuffer;
import java.util.Random;
public class MemPressureTest {
static final int SIZE = 4096;
@State(Scope.Benchmark)
public static class Bigish {
final ByteBuffer b;
private Blackhole blackhole;
@Setup(Level.Trial)
public void setup(Blackhole blackhole) {
this.blackhole = blackhole;
}
public Bigish() {
this.b = ByteBuffer.allocate(SIZE);
}
public void fill(byte bt) {
b.clear();
for (int i = 0; i < SIZE; ++i) {
b.put(bt);
}
blackhole.consume(b);
}
}
static Random random = new Random(1);
@Benchmark
public static void test1(Blackhole blackhole) {
Bigish tmp = new Bigish();
tmp.setup(blackhole);
tmp.fill((byte)random.nextInt(255));
blackhole.consume(tmp);
}
@Benchmark
public static void test2(Bigish perm) {
perm.fill((byte) random.nextInt(255));
}
}
不过,第二次测试要慢得多:
> Task :jmh
# JMH version: 1.35
# VM version: JDK 18.0.1.1, OpenJDK 64-Bit Server VM, 18.0.1.1+2-6
# VM invoker: /Users/xxx/Library/Java/JavaVirtualMachines/openjdk-18.0.1.1/Contents/Home/bin/java
# VM options: -Dfile.encoding=UTF-8 -Djava.io.tmpdir=/Users/xxx/Dev/MemTests/build/tmp/jmh -Duser.country=US -Duser.language=en -Duser.variant
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 5 iterations, 10 s each
# Measurement: 5 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.xxx.MemPressureTest.test1
# Run progress: 0.00% complete, ETA 00:16:40
# Fork: 1 of 5
# Warmup Iteration 1: 2183998.556 ops/s
# Warmup Iteration 2: 2281885.941 ops/s
# Warmup Iteration 3: 2239644.018 ops/s
# Warmup Iteration 4: 1608047.994 ops/s
# Warmup Iteration 5: 1992314.001 ops/s
Iteration 1: 2053657.571 ops/s3s]
Iteration 2: 2054957.773 ops/sm 3s]
Iteration 3: 2051595.233 ops/sm 13s]
Iteration 4: 2054878.239 ops/sm 23s]
Iteration 5: 2031111.214 ops/sm 33s]
# Run progress: 10.00% complete, ETA 00:15:04
# Fork: 2 of 5
# Warmup Iteration 1: 2228594.345 ops/s
# Warmup Iteration 2: 2257983.355 ops/s
# Warmup Iteration 3: 2063130.244 ops/s
# Warmup Iteration 4: 1629084.669 ops/s
# Warmup Iteration 5: 2063018.496 ops/s
Iteration 1: 1939260.937 ops/sm 33s]
Iteration 2: 1791414.018 ops/sm 43s]
Iteration 3: 1914987.221 ops/sm 53s]
Iteration 4: 1969484.898 ops/sm 3s]
Iteration 5: 1891440.624 ops/sm 13s]
# Run progress: 20.00% complete, ETA 00:13:23
# Fork: 3 of 5
# Warmup Iteration 1: 2228664.719 ops/s
# Warmup Iteration 2: 2263677.403 ops/s
# Warmup Iteration 3: 2237032.464 ops/s
# Warmup Iteration 4: 2040040.243 ops/s
# Warmup Iteration 5: 2038848.530 ops/s
Iteration 1: 2023934.952 ops/sm 14s]
Iteration 2: 2041874.437 ops/sm 24s]
Iteration 3: 2002858.770 ops/sm 34s]
Iteration 4: 2039727.607 ops/sm 44s]
Iteration 5: 2045827.670 ops/sm 54s]
# Run progress: 30.00% complete, ETA 00:11:43
# Fork: 4 of 5
# Warmup Iteration 1: 2105430.688 ops/s
# Warmup Iteration 2: 2279387.762 ops/s
# Warmup Iteration 3: 2228346.691 ops/s
# Warmup Iteration 4: 1438607.183 ops/s
# Warmup Iteration 5: 2059319.745 ops/s
Iteration 1: 1112543.932 ops/sm 54s]
Iteration 2: 1977077.976 ops/sm 4s]
Iteration 3: 2040147.355 ops/sm 14s]
Iteration 4: 1975766.032 ops/sm 24s]
Iteration 5: 2003532.092 ops/sm 34s]
# Run progress: 40.00% complete, ETA 00:10:02
# Fork: 5 of 5
# Warmup Iteration 1: 2240203.848 ops/s
# Warmup Iteration 2: 2245673.994 ops/s
# Warmup Iteration 3: 2096257.864 ops/s
# Warmup Iteration 4: 2046527.740 ops/s
# Warmup Iteration 5: 2050379.941 ops/s
Iteration 1: 2050691.989 ops/sm 35s]
Iteration 2: 2057803.100 ops/sm 45s]
Iteration 3: 2058634.766 ops/sm 55s]
Iteration 4: 2060596.595 ops/sm 5s]
Iteration 5: 2061282.107 ops/sm 15s]
Result "com.xxx.MemPressureTest.test1":
1972203.484 ±(99.9%) 142904.698 ops/s [Average]
(min, avg, max) = (1112543.932, 1972203.484, 2061282.107), stdev = 190773.683
CI (99.9%): [1829298.786, 2115108.182] (assumes normal distribution)
# JMH version: 1.35
# VM version: JDK 18.0.1.1, OpenJDK 64-Bit Server VM, 18.0.1.1+2-6
# VM invoker: /Users/xxx/Library/Java/JavaVirtualMachines/openjdk-18.0.1.1/Contents/Home/bin/java
# VM options: -Dfile.encoding=UTF-8 -Djava.io.tmpdir=/Users/xxx/Dev/MemTests/build/tmp/jmh -Duser.country=US -Duser.language=en -Duser.variant
# Blackhole mode: compiler (auto-detected, use -Djmh.blackhole.autoDetect=false to disable)
# Warmup: 5 iterations, 10 s each
# Measurement: 5 iterations, 10 s each
# Timeout: 10 min per iteration
# Threads: 1 thread, will synchronize iterations
# Benchmark mode: Throughput, ops/time
# Benchmark: com.xxx.MemPressureTest.test2
# Run progress: 50.00% complete, ETA 00:08:22
# Fork: 1 of 5
# Warmup Iteration 1: 282751.407 ops/s
# Warmup Iteration 2: 283333.984 ops/s
# Warmup Iteration 3: 293785.079 ops/s
# Warmup Iteration 4: 268403.105 ops/s
# Warmup Iteration 5: 280054.277 ops/s
Iteration 1: 279093.118 ops/s9m 15s]
Iteration 2: 282782.996 ops/s9m 25s]
Iteration 3: 282688.921 ops/s9m 35s]
Iteration 4: 291578.963 ops/s9m 45s]
Iteration 5: 294835.777 ops/s9m 55s]
# Run progress: 60.00% complete, ETA 00:06:41
# Fork: 2 of 5
# Warmup Iteration 1: 283735.550 ops/s
# Warmup Iteration 2: 283536.547 ops/s
# Warmup Iteration 3: 294403.173 ops/s
# Warmup Iteration 4: 284161.042 ops/s
# Warmup Iteration 5: 281719.077 ops/s
Iteration 1: 276838.416 ops/s10m 56s]
Iteration 2: 284063.117 ops/s11m 6s]
Iteration 3: 282361.985 ops/s11m 16s]
Iteration 4: 289125.092 ops/s11m 26s]
Iteration 5: 294236.625 ops/s11m 36s]
# Run progress: 70.00% complete, ETA 00:05:01
# Fork: 3 of 5
# Warmup Iteration 1: 284567.336 ops/s
# Warmup Iteration 2: 283548.713 ops/s
# Warmup Iteration 3: 294317.511 ops/s
# Warmup Iteration 4: 283501.873 ops/s
# Warmup Iteration 5: 283691.306 ops/s
Iteration 1: 283462.749 ops/s12m 36s]
Iteration 2: 284120.587 ops/s12m 46s]
Iteration 3: 264878.952 ops/s12m 56s]
Iteration 4: 292681.168 ops/s13m 6s]
Iteration 5: 295279.759 ops/s13m 16s]
# Run progress: 80.00% complete, ETA 00:03:20
# Fork: 4 of 5
# Warmup Iteration 1: 284823.519 ops/s
# Warmup Iteration 2: 283913.207 ops/s
# Warmup Iteration 3: 294401.483 ops/s
# Warmup Iteration 4: 283998.027 ops/s
# Warmup Iteration 5: 283987.408 ops/s
Iteration 1: 278014.618 ops/s14m 17s]
Iteration 2: 283431.491 ops/s14m 27s]
Iteration 3: 284465.945 ops/s14m 37s]
Iteration 4: 293202.934 ops/s14m 47s]
Iteration 5: 290059.807 ops/s14m 57s]
# Run progress: 90.00% complete, ETA 00:01:40
# Fork: 5 of 5
# Warmup Iteration 1: 285598.809 ops/s
# Warmup Iteration 2: 284434.916 ops/s
# Warmup Iteration 3: 294355.547 ops/s
# Warmup Iteration 4: 284307.860 ops/s
# Warmup Iteration 5: 284297.362 ops/s
Iteration 1: 283676.043 ops/s15m 57s]
Iteration 2: 283609.750 ops/s16m 7s]
Iteration 3: 284575.124 ops/s16m 17s]
Iteration 4: 293564.269 ops/s16m 27s]
Iteration 5: 216267.883 ops/s16m 37s]
Result "com.xxx.MemPressureTest.test2":
282755.844 ±(99.9%) 11599.112 ops/s [Average]
(min, avg, max) = (216267.883, 282755.844, 295279.759), stdev = 15484.483
CI (99.9%): [271156.731, 294354.956] (assumes normal distribution)
JMH黑洞应该阻止代码删除,而JMH现在负责运行独立迭代的事实应该阻止并行化,对吗?黑洞不也应该阻止物体被限制在堆栈里吗?此外,如果hotspot仍然在进行大量的优化,那么预热迭代之间不会有更多的变化吗?
你最初的问题和编辑过的JMH版本实际上略有不同。
在编辑的版本中,就像@apangin提到的,存储在静态字段中的指针烫发
会阻止代码得到优化。
在你最初的问题中,是因为你忘记热身了。这是一个修改后的版本:
public static void main(String[] args) {
var t1 = System.currentTimeMillis();
var warmup = Integer.parseInt(args[0]);
for (int i = 0; i < warmup; i++) { test(1); } // magic!!!
test(1000000);
var t2 = System.currentTimeMillis();
System.out.println(t2 - t1);
}
private static void test(int n) {
Random random = new Random(1);
Bigish tmp = new Bigish();
for (int i = 0; i < n; ++i) {
tmp.fill((byte) random.nextInt(255));
}
}
它需要一个int参数热身
来帮助JVM决定哪些方法应该内联。
在我的机器上,也就是OpenJDK Runtime Technology Zulu17.28 13-CA(build 17 35-LTS)
在Windows上,当预热
为8000
时,输出是不可预测的。通常需要~2.7秒,但偶尔只需要110毫秒。
当< code>warmup设置为< code>8500时,它几乎总是在110~120毫秒内完成。
您还可以使用-XX: UnlockDiagnostiVMOptions-XX: PrintInline
选项来查看JVM如何内联方法。如果所有内容都完全内联,您应该能够看到类似的内容
@ 24 A$Bigish::<init> (11 bytes) inline (hot)
@ 4 java.nio.ByteBuffer::allocate (20 bytes) inline (hot)
@ 16 java.nio.HeapByteBuffer::<init> (21 bytes) inline (hot)
@ 10 java.nio.ByteBuffer::<init> (47 bytes) inline (hot)
@ 8 java.nio.Buffer::<init> (105 bytes) inline (hot)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
@ 39 java.nio.ByteBuffer::limit (6 bytes) inline (hot)
@ 2 java.nio.ByteBuffer::limit (8 bytes) inline (hot)
@ 2 java.nio.Buffer::limit (65 bytes) inline (hot)
@ 45 java.nio.ByteBuffer::position (6 bytes) inline (hot)
@ 2 java.nio.ByteBuffer::position (8 bytes) inline (hot)
@ 2 java.nio.Buffer::position (52 bytes) inline (hot)
@ 17 java.nio.ByteOrder::nativeOrder (4 bytes) inline (hot)
@ 7 A$Bigish::<init> (10 bytes) inline (hot)
@ 1 java.lang.Object::<init> (1 bytes) inline (hot)
靠近输出的底部。
请注意,只有当Bigish
和ByteBuffer
constructor完全内联时,JVM才能断言底层缓冲区对另一个线程永远不可见,这允许对缓冲区的写入被安全矢量化。
顺便说一句,这是又一个显示基准测试是多么棘手的案例。如果不深入细节,很难判断哪个部分是真正的性能瓶颈。甚至JMH也可能会误导人。
以下只是一个理论,他们可能完全错误。我既不是JIT也不是GC专家。
我认为JIT优化掉了(一些)你的代码。如果是这种情况,它检测到您实际上没有使用存储的值,只是删除了分配/填充对象的代码。像JmH黑洞这样的东西可能会帮助你。
也可能是它并行化了代码的情况。由于循环不同的迭代彼此独立,因此可以并行执行多个迭代。
另一种可能性是,它检测到对象受到堆栈限制,范围非常窄,并立即删除。因此,它可能已将对象移动到堆栈中,在那里可以快速分配/推送和取消分配/弹出对象。
JIT 可能总是会做意想不到的事情。不要过早地优化,也不要猜测瓶颈在哪里。在您进行任何更改之前和之后衡量您的表现。性能可能不会丢失您期望的性能。在其他语言中也是如此,尤其是在Java中。
而且,正如评论中提到的,你真的应该使用JMH。
在填充之前创建新的ByteBuffer确实有助于JIT编译器在使用相对put
方法时生成更好的优化代码,原因如下。
test1
方法时,缓冲区实例化与填充出现在同一编译范围内:Bigish tmp = new Bigish();
tmp.setup(blackhole);
tmp.fill((byte)random.nextInt(255));
JVM知道关于创建的缓冲区的一切:它的确切大小和支持数组,它知道缓冲区尚未发布,其他线程也看不到它。因此,JVM可以极大地优化填充循环:使用AVX指令将其矢量化,并将其展开以一次设置512字节:
0x000001cdf60c9ae0: mov %r9d,%r8d
0x000001cdf60c9ae3: movslq %r8d,%r9
0x000001cdf60c9ae6: add %r11,%r9
0x000001cdf60c9ae9: vmovdqu %ymm0,0x10(%rcx,%r9,1)
0x000001cdf60c9af0: vmovdqu %ymm0,0x30(%rcx,%r9,1)
0x000001cdf60c9af7: vmovdqu %ymm0,0x50(%rcx,%r9,1)
0x000001cdf60c9afe: vmovdqu %ymm0,0x70(%rcx,%r9,1)
0x000001cdf60c9b05: vmovdqu %ymm0,0x90(%rcx,%r9,1)
0x000001cdf60c9b0f: vmovdqu %ymm0,0xb0(%rcx,%r9,1)
0x000001cdf60c9b19: vmovdqu %ymm0,0xd0(%rcx,%r9,1)
0x000001cdf60c9b23: vmovdqu %ymm0,0xf0(%rcx,%r9,1)
0x000001cdf60c9b2d: vmovdqu %ymm0,0x110(%rcx,%r9,1)
0x000001cdf60c9b37: vmovdqu %ymm0,0x130(%rcx,%r9,1)
0x000001cdf60c9b41: vmovdqu %ymm0,0x150(%rcx,%r9,1)
0x000001cdf60c9b4b: vmovdqu %ymm0,0x170(%rcx,%r9,1)
0x000001cdf60c9b55: vmovdqu %ymm0,0x190(%rcx,%r9,1)
0x000001cdf60c9b5f: vmovdqu %ymm0,0x1b0(%rcx,%r9,1)
0x000001cdf60c9b69: vmovdqu %ymm0,0x1d0(%rcx,%r9,1)
0x000001cdf60c9b73: vmovdqu %ymm0,0x1f0(%rcx,%r9,1)
0x000001cdf60c9b7d: mov %r8d,%r9d
0x000001cdf60c9b80: add $0x200,%r9d
0x000001cdf60c9b87: cmp %r10d,%r9d
0x000001cdf60c9b8a: jl 0x000001cdf60c9ae0
Bigish tmp = new Bigish();
volatileField = tmp; // publish
tmp.setup(blackhole);
tmp.fill((byte)random.nextInt(255));
循环优化中断;现在,数组字节被逐个填充,位置字段也相应地递增。
0x000001829b18ca5c: nopl 0x0(%rax)
0x000001829b18ca60: cmp %r11d,%esi
0x000001829b18ca63: jge 0x000001829b18ce34 ;*if_icmplt {reexecute=0 rethrow=0 return_oop=0}
; - java.nio.Buffer::nextPutIndex@10 (line 721)
; - java.nio.HeapByteBuffer::put@6 (line 209)
; - bench.MemPressureTest$Bigish::fill@22 (line 33)
; - bench.MemPressureTest::test1@28 (line 47)
0x000001829b18ca69: mov %esi,%ecx
0x000001829b18ca6b: add %edx,%ecx ;*getfield position {reexecute=0 rethrow=0 return_oop=0}
; - java.nio.Buffer::nextPutIndex@1 (line 720)
; - java.nio.HeapByteBuffer::put@6 (line 209)
; - bench.MemPressureTest$Bigish::fill@22 (line 33)
; - bench.MemPressureTest::test1@28 (line 47)
0x000001829b18ca6d: mov %esi,%eax
0x000001829b18ca6f: inc %eax ;*iinc {reexecute=0 rethrow=0 return_oop=0}
; - bench.MemPressureTest$Bigish::fill@26 (line 32)
; - bench.MemPressureTest::test1@28 (line 47)
0x000001829b18ca71: mov %eax,0x18(%r10) ;*putfield position {reexecute=0 rethrow=0 return_oop=0}
; - java.nio.Buffer::nextPutIndex@25 (line 723)
; - java.nio.HeapByteBuffer::put@6 (line 209)
; - bench.MemPressureTest$Bigish::fill@22 (line 33)
; - bench.MemPressureTest::test1@28 (line 47)
0x000001829b18ca75: cmp %r8d,%ecx
0x000001829b18ca78: jae 0x000001829b18ce14
0x000001829b18ca7e: movslq %esi,%r9
0x000001829b18ca81: add %r14,%r9
0x000001829b18ca84: mov %bl,0x10(%rdi,%r9,1) ;*bastore {reexecute=0 rethrow=0 return_oop=0}
; - java.nio.HeapByteBuffer::put@13 (line 209)
; - bench.MemPressureTest$Bigish::fill@22 (line 33)
; - bench.MemPressureTest::test1@28 (line 47)
0x000001829b18ca89: cmp $0x1000,%eax
0x000001829b18ca8f: jge 0x000001829b18ca95 ;*if_icmpge {reexecute=0 rethrow=0 return_oop=0}
; - bench.MemPressureTest$Bigish::fill@14 (line 32)
; - bench.MemPressureTest::test1@28 (line 47)
0x000001829b18ca91: mov %eax,%esi
0x000001829b18ca93: jmp 0x000001829b18ca5c
这正是测试2
中发生的事情。由于 ByteBuffer 对象位于编译范围之外,因此 JIT 无法像本地尚未发布的对象那样自由地优化它。
好消息是,这是可能的。只需使用绝对< code>put方法,而不是相对方法。在这种情况下,< code>position字段保持不变,JIT可以轻松地对循环进行矢量化,而没有破坏ByteBuffer不变量的风险。
for (int i = 0; i < SIZE; ++i) {
b.put(i, bt);
}
通过这种改变,在两种情况下循环都将被矢量化。更好的是,现在< code>test2变得比< code>test1快得多,证明对象创建确实有性能开销。
Benchmark Mode Cnt Score Error Units
MemPressureTest.test1 thrpt 10 2447,370 ± 146,804 ops/ms
MemPressureTest.test2 thrpt 10 15677,575 ± 136,075 ops/ms
在我们的kafka broker设置中,GC平均需要20毫秒,但随机增加到1-2秒。极端情况持续9秒。这种情况的发生频率相当随机。平均每天发生15次。我尝试过使用GCEasy,但没有给出任何见解。我的内存使用率为20%,但进程仍然使用交换,尽管内存可用。感谢您对如何将其最小化的任何意见 JVM选择: GC日志:
问题内容: 在下面的示例中,new Thread()没有任何引用。可能是被废弃的垃圾收集了吗?同样,在不扩展Thread类或实现可运行的情况下,我们如何创建线程? 问题答案: 如果尚未启动的新线程无法正常访问,则将对其进行垃圾回收。 已经启动的新线程成为垃圾回收“根”。它不会(直到)完成才被垃圾收集。 在下面的示例中,new Thread()没有任何引用。可能是被废弃的垃圾收集了吗? 不。它已经启
我使用dotMemory来分析我的应用程序,我注意到下面的行为:在我的代码中有一些点,我通过使用手动执行垃圾回收机制 在dotMemory内部,我看到内存实际上在这些点上被释放了,但是如果在那之后我点击“强制气相色谱”,就会收集更多的垃圾。他们这样做的方式是什么,为什么我的代码没有收集内存,是否有可能实现相同级别的收集? 我还尝试执行多个集合,即。 尽管它似乎回收了更多的内存,但它从未接近dotM
问题内容: 我很难处理Java垃圾回收问题并解释日志。 我的应用程序要求GC的时间不要超过2秒,理想情况下是少于100ms。 根据先前的一些建议,我正在尝试以下命令行选项: 该应用程序具有大量长期存储的对象,这些对象保存在ConcurrentLinkedHashMap中。我偶尔会出现长时间的停顿,在最坏的情况下可能会长达10秒(这是倒数第二次,如下面的GC日志所示)! 这是我得到的一些输出: 我已
问题内容: 是什么决定了垃圾收集器何时真正收集?它是在一定时间之后还是在一定数量的内存用完之后发生的吗?还是还有其他因素? 问题答案: 它在确定是时候运行时运行。在世代垃圾收集器中,一种常见的策略是在第0代内存分配失败时运行收集器。也就是说,每次你分配一小块内存(大块通常直接放置在“旧”代中)时,系统都会检查gen-0堆中是否有足够的可用空间,如果没有,则运行GC释放空间以使分配成功。然后将旧数据
问题内容: 我经常读到,在Sun JVM中,短寿命对象(“相对较新的对象”)比长寿命对象(“相对较旧的对象”)可以更有效地进行垃圾回收。 为什么呢? 这是特定于Sun JVM还是由一般的垃圾回收原理导致? 问题答案: 大多数Java应用程序都会创建Java对象,然后很快将其丢弃。您可以在方法中创建一些对象,然后一旦退出该方法,所有对象都会死亡。大多数应用程序都是以这种方式运行的,并且大多数人倾向于