单字符字符串列表的内存分配率高于多字符字符串列表
Higher memory allocation rates for List of single-character String than multi-character String
考虑以下基准,它分配 List
的 String
长度 1 与长度 8
@State(Scope.Benchmark)
@BenchmarkMode(Array(Mode.Throughput))
class SoMemory {
val size = 1_000_000
@Benchmark def a: List[String] = List.fill[String](size)(Random.nextString(1))
@Benchmark def b: List[String] = List.fill[String](size)(Random.nextString(8))
}
其中 sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 -prof gc bench.SoMemory"
给出
[info] Benchmark Mode Cnt Score Error Units
[info] SoMemory.a thrpt 20 16.650 ± 0.519 ops/s
[info] SoMemory.a:·gc.alloc.rate thrpt 20 3870.364 ± 120.687 MB/sec
[info] SoMemory.a:·gc.alloc.rate.norm thrpt 20 255963282.822 ± 61.012 B/op
[info] SoMemory.a:·gc.churn.PS_Eden_Space thrpt 20 3862.090 ± 161.598 MB/sec
[info] SoMemory.a:·gc.churn.PS_Eden_Space.norm thrpt 20 255331784.446 ± 4839869.981 B/op
[info] SoMemory.a:·gc.churn.PS_Survivor_Space thrpt 20 25.893 ± 1.433 MB/sec
[info] SoMemory.a:·gc.churn.PS_Survivor_Space.norm thrpt 20 1711320.051 ± 64870.177 B/op
[info] SoMemory.a:·gc.count thrpt 20 318.000 counts
[info] SoMemory.a:·gc.time thrpt 20 45183.000 ms
[info] SoMemory.b thrpt 20 2.859 ± 0.092 ops/s
[info] SoMemory.b:·gc.alloc.rate thrpt 20 2763.961 ± 89.654 MB/sec
[info] SoMemory.b:·gc.alloc.rate.norm thrpt 20 1063705990.899 ± 503.169 B/op
[info] SoMemory.b:·gc.churn.PS_Eden_Space thrpt 20 2768.433 ± 101.742 MB/sec
[info] SoMemory.b:·gc.churn.PS_Eden_Space.norm thrpt 20 1065601049.380 ± 25878705.006 B/op
[info] SoMemory.b:·gc.churn.PS_Survivor_Space thrpt 20 20.838 ± 1.063 MB/sec
[info] SoMemory.b:·gc.churn.PS_Survivor_Space.norm thrpt 20 8015328.037 ± 236873.550 B/op
[info] SoMemory.b:·gc.count thrpt 20 234.000 counts
[info] SoMemory.b:·gc.time thrpt 20 37696.000 ms
请注意更小的字符串有更高的 gc.alloc.rate
SoMemory.a:·gc.alloc.rate thrpt 20 3870.364 ± 120.687 MB/sec
SoMemory.b:·gc.alloc.rate thrpt 20 2763.961 ± 89.654 MB/sec
为什么在第一种情况下似乎有更高的内存消耗,当较小的字符串应该有较小的内存占用时,例如,JOL 给出
class ZarA { val x = List.fill[String](1_000_000)(Random.nextString(1)) }
class ZarB { val x = List.fill[String](1_000_000)(Random.nextString(8)) }
正如预期的那样,ZarA
的占用空间更小,约为 72MB
example.ZarA@15975490d footprint:
COUNT AVG SUM DESCRIPTION
1000000 24 24000000 [C
1 16 16 example.ZarA
1000000 24 24000000 java.lang.String
1000000 24 24000000 scala.collection.immutable.$colon$colon
1 16 16 scala.collection.immutable.Nil$
3000002 72000032 (total)
与 ZarB
约 80MB 的较大占用空间相比
example.ZarB@15975490d footprint:
COUNT AVG SUM DESCRIPTION
1000000 32 32000000 [C
1 16 16 example.ZarB
1000000 24 24000000 java.lang.String
1000000 24 24000000 scala.collection.immutable.$colon$colon
1 16 16 scala.collection.immutable.Nil$
3000002 80000032 (total)
VisualVM 内存行为
ZarA - 已用堆 129 MB
ZarB - 已用堆 91 MB
分配率是分配内存的速度(每单位时间分配的内存量)。它没有告诉我们有关分配的总内存的任何信息。
找到较小的连续内存区域总是比较大的连续内存区域更容易,例如分配例如1000 个长度为 1 的字符串应该比分配(例如)花费更少的时间。 1000 个长度为 8 的字符串,导致更高的分配率和更少的总内存消耗。
考虑以下基准,它分配 List
的 String
长度 1 与长度 8
@State(Scope.Benchmark)
@BenchmarkMode(Array(Mode.Throughput))
class SoMemory {
val size = 1_000_000
@Benchmark def a: List[String] = List.fill[String](size)(Random.nextString(1))
@Benchmark def b: List[String] = List.fill[String](size)(Random.nextString(8))
}
其中 sbt "jmh:run -i 10 -wi 10 -f 2 -t 1 -prof gc bench.SoMemory"
给出
[info] Benchmark Mode Cnt Score Error Units
[info] SoMemory.a thrpt 20 16.650 ± 0.519 ops/s
[info] SoMemory.a:·gc.alloc.rate thrpt 20 3870.364 ± 120.687 MB/sec
[info] SoMemory.a:·gc.alloc.rate.norm thrpt 20 255963282.822 ± 61.012 B/op
[info] SoMemory.a:·gc.churn.PS_Eden_Space thrpt 20 3862.090 ± 161.598 MB/sec
[info] SoMemory.a:·gc.churn.PS_Eden_Space.norm thrpt 20 255331784.446 ± 4839869.981 B/op
[info] SoMemory.a:·gc.churn.PS_Survivor_Space thrpt 20 25.893 ± 1.433 MB/sec
[info] SoMemory.a:·gc.churn.PS_Survivor_Space.norm thrpt 20 1711320.051 ± 64870.177 B/op
[info] SoMemory.a:·gc.count thrpt 20 318.000 counts
[info] SoMemory.a:·gc.time thrpt 20 45183.000 ms
[info] SoMemory.b thrpt 20 2.859 ± 0.092 ops/s
[info] SoMemory.b:·gc.alloc.rate thrpt 20 2763.961 ± 89.654 MB/sec
[info] SoMemory.b:·gc.alloc.rate.norm thrpt 20 1063705990.899 ± 503.169 B/op
[info] SoMemory.b:·gc.churn.PS_Eden_Space thrpt 20 2768.433 ± 101.742 MB/sec
[info] SoMemory.b:·gc.churn.PS_Eden_Space.norm thrpt 20 1065601049.380 ± 25878705.006 B/op
[info] SoMemory.b:·gc.churn.PS_Survivor_Space thrpt 20 20.838 ± 1.063 MB/sec
[info] SoMemory.b:·gc.churn.PS_Survivor_Space.norm thrpt 20 8015328.037 ± 236873.550 B/op
[info] SoMemory.b:·gc.count thrpt 20 234.000 counts
[info] SoMemory.b:·gc.time thrpt 20 37696.000 ms
请注意更小的字符串有更高的 gc.alloc.rate
SoMemory.a:·gc.alloc.rate thrpt 20 3870.364 ± 120.687 MB/sec
SoMemory.b:·gc.alloc.rate thrpt 20 2763.961 ± 89.654 MB/sec
为什么在第一种情况下似乎有更高的内存消耗,当较小的字符串应该有较小的内存占用时,例如,JOL 给出
class ZarA { val x = List.fill[String](1_000_000)(Random.nextString(1)) }
class ZarB { val x = List.fill[String](1_000_000)(Random.nextString(8)) }
正如预期的那样,ZarA
example.ZarA@15975490d footprint:
COUNT AVG SUM DESCRIPTION
1000000 24 24000000 [C
1 16 16 example.ZarA
1000000 24 24000000 java.lang.String
1000000 24 24000000 scala.collection.immutable.$colon$colon
1 16 16 scala.collection.immutable.Nil$
3000002 72000032 (total)
与 ZarB
example.ZarB@15975490d footprint:
COUNT AVG SUM DESCRIPTION
1000000 32 32000000 [C
1 16 16 example.ZarB
1000000 24 24000000 java.lang.String
1000000 24 24000000 scala.collection.immutable.$colon$colon
1 16 16 scala.collection.immutable.Nil$
3000002 80000032 (total)
VisualVM 内存行为
ZarA - 已用堆 129 MB
ZarB - 已用堆 91 MB
分配率是分配内存的速度(每单位时间分配的内存量)。它没有告诉我们有关分配的总内存的任何信息。
找到较小的连续内存区域总是比较大的连续内存区域更容易,例如分配例如1000 个长度为 1 的字符串应该比分配(例如)花费更少的时间。 1000 个长度为 8 的字符串,导致更高的分配率和更少的总内存消耗。