专业编程基础技术教程

网站首页 > 基础教程 正文

ArrayList 插入1000万条数据之后,我怀疑了jvm...

ccvgpt 2024-10-12 14:05:18 基础教程 10 ℃

我发现新大陆了,等会发你代码" "咋了,这么激动" "等会..."

List
<
Integer
> list0 = 
new
 
ArrayList
<
Integer
>();
long
 start0 = 
System
.currentTimeMillis();
for
 (
int
 i = 
0
; i < 
10000000
; i++) {
 list0.add(i);
}
System
.
out
.println(
System
.currentTimeMillis() - start0);
long
 start1 = 
System
.currentTimeMillis();
List
<
Integer
> list1 = 
new
 
ArrayList
<
Integer
>();
for
 (
int
 i = 
10000000
; i < 
20000000
; i++) {
 list1.add(i);
}
System
.
out
.println(
System
.currentTimeMillis() - start1);

"我在一个ArrayList中连续插入1千万条数据,结果耗时不一样,分别是 2346 797 没搞明白"

ArrayList 插入1000万条数据之后,我怀疑了jvm...

我看了一眼,就知道这小伙底盘不稳。

"你加个 -XX:+PrintGCDetails -XX:+PrintGCDateStamps,看下第一次是不是有Full GC"

"明白,我再试试看"

几分钟后...

2019
-
09
-
28T09
:
49
:
07.519
-
0800
:
 
[
GC 
(
Allocation
 
Failure
)
 
[
PSYoungGen
:
 
54888K
->
10738K
(
76288K
)]
 
54888K
->
36180K
(
251392K
),
 
0.0520111
 secs
]
 
[
Times
:
 user
=
0.24
 sys
=
0.03
,
 real
=
0.06
 secs
]
 
2019
-
09
-
28T09
:
49
:
07.590
-
0800
:
 
[
GC 
(
Allocation
 
Failure
)
 
[
PSYoungGen
:
 
74092K
->
10736K
(
141824K
)]
 
99534K
->
80803K
(
316928K
),
 
0.0693607
 secs
]
 
[
Times
:
 user
=
0.39
 sys
=
0.03
,
 real
=
0.06
 secs
]
 
2019
-
09
-
28T09
:
49
:
07.751
-
0800
:
 
[
GC 
(
Allocation
 
Failure
)
 
[
PSYoungGen
:
 
141808K
->
10736K
(
141824K
)]
 
211875K
->
188026K
(
320512K
),
 
0.1829926
 secs
]
 
[
Times
:
 user
=
1.02
 sys
=
0.10
,
 real
=
0.18
 secs
]
 
2019
-
09
-
28T09
:
49
:
07.934
-
0800
:
 
[
Full
 GC 
(
Ergonomics
)
 
[
PSYoungGen
:
 
10736K
->
0K
(
141824K
)]
 
[
ParOldGen
:
 
177290K
->
171620K
(
402432K
)]
 
188026K
->
171620K
(
544256K
),
 
[
Metaspace
:
 
3062K
->
3062K
(
1056768K
)],
 
1.8672996
 secs
]
 
[
Times
:
 user
=
5.96
 sys
=
0.03
,
 real
=
1.87
 secs
]
 
2365
2019
-
09
-
28T09
:
49
:
09.832
-
0800
:
 
[
GC 
(
Allocation
 
Failure
)
 
[
PSYoungGen
:
 
129254K
->
10738K
(
196608K
)]
 
300875K
->
282609K
(
599040K
),
 
0.1039307
 secs
]
 
[
Times
:
 user
=
0.74
 sys
=
0.07
,
 real
=
0.10
 secs
]
 
2019
-
09
-
28T09
:
49
:
09.936
-
0800
:
 
[
Full
 GC 
(
Ergonomics
)
 
[
PSYoungGen
:
 
10738K
->
0K
(
196608K
)]
 
[
ParOldGen
:
 
271871K
->
36047K
(
372736K
)]
 
282609K
->
36047K
(
569344K
),
 
[
Metaspace
:
 
3067K
->
3067K
(
1056768K
)],
 
0.4510440
 secs
]
 
[
Times
:
 user
=
1.82
 sys
=
0.01
,
 real
=
0.45
 secs
]
 
2019
-
09
-
28T09
:
49
:
10.440
-
0800
:
 
[
GC 
(
Allocation
 
Failure
)
 
[
PSYoungGen
:
 
185856K
->
10752K
(
264704K
)]
 
221903K
->
171359K
(
637440K
),
 
0.1292143
 secs
]
 
[
Times
:
 user
=
0.97
 sys
=
0.01
,
 real
=
0.12
 secs
]
 
772

"第一次Full GC果然耗时了1.87s,那我把堆调大看看,避免Full GC"

几分钟后...

"这次没有GC了,但是每次运行,前一个都比后一个耗时多点,这是怎么回事?"

"你试试放在不同线程中运行?"

"好"

又几分钟后...

"在不同线程中执行,两者耗时几乎一致,这是为什么?"

"你知道OSR吗?"

"不知道."

"那我跟你大概讲讲."

OSR(On-Stack Replacement ),是一种在运行时替换正在运行的函数/方法的栈帧的技术。

在现代的主流JVM中,都具备了多层编译的能力,一开始以解释的方式进行执行,这种性能相对来说(和c++比)会慢一点

但是一旦发现某一个函数执行很频繁的时候,就会采用JIT编译,提高函数执行性能(大部分比c++还快)。

但是,如果以函数为单位进行JIT编译,那么就无法应对main函数中包含循环体的情况,这个时候,OSR就派上了用场。

与其编译整个方法,我们可以在发现某个方法里有循环很热的时候,选择只编译方法里的某个循环,当循环体执行到 i = 5000 的时候,循环计数器达到了触发OSR编译的阈值,等编译完成之后,就可以执行编译后生成的代码。

所以在上面例子中,当我们第二次执行循环体的时候,已经在执行OSR编译后的代码,那么在性能上会比前一次会快那么一点点。

Tags:

最近发表
标签列表