简体   繁体   English

为什么GC无法在同一方法中运行

[英]Why GC doesn't run within the same method

I'm playing with gc and found interesting situation when gc(I use parallel gc) isn't involved This is my code 我在玩gc并发现不涉及gc(我使用并行gc)的情况,这是我的代码

public static void main(String[] args) {
       System.out.println(Runtime.getRuntime().freeMemory() / (1024 * 1024) + " free");

    String[] strings = new String[(40 * 1024 * 1024) / Integer.BYTES];
    System.out.println(strings.length);

    System.out.println(Runtime.getRuntime().freeMemory() / (1024 * 1024) + " free");

    strings = new String[(40 * 1024 * 1024) / Integer.BYTES];
    System.out.println(strings.length);

    System.out.println(Runtime.getRuntime().freeMemory() / (1024 * 1024) + " free");

    }

My Java version is : 我的Java版本是:

java version "1.8.0_191"
Java(TM) SE Runtime Environment (build 1.8.0_191-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode)

This program was involved with the following argc: 该程序涉及以下argc:

-Xmx64m -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps

This is the output: 这是输出:

59 free
10485760
19 free
2019-06-09T14:53:18.868+0600: 0.105: [GC (Allocation Failure) [PSYoungGen: 1640K->480K(18944K)] 42600K->41448K(62976K), 0.0018199 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
2019-06-09T14:53:18.870+0600: 0.107: [Full GC (Ergonomics) [PSYoungGen: 480K->0K(18944K)] [ParOldGen: 40968K->41339K(44032K)] 41448K->41339K(62976K), [Metaspace: 3033K->3033K(1056768K)], 0.0174681 secs] [Times: user=0.04 sys=0.00, real=0.02 secs] 
2019-06-09T14:53:18.888+0600: 0.125: [GC (Allocation Failure) [PSYoungGen: 0K->0K(18944K)] 41339K->41339K(62976K), 0.0017270 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
2019-06-09T14:53:18.889+0600: 0.126: [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(18944K)] [ParOldGen: 41339K->41321K(44032K)] 41339K->41321K(62976K), [Metaspace: 3033K->3033K(1056768K)], 0.0140842 secs] [Times: user=0.03 sys=0.01, real=0.01 secs] 
Heap
 PSYoungGen      total 18944K, used 491K [0x00000000feb00000, 0x0000000100000000, 0x0000000100000000)
  eden space 16384K, 3% used [0x00000000feb00000,0x00000000feb7afa0,0x00000000ffb00000)
  from space 2560K, 0% used [0x00000000ffd80000,0x00000000ffd80000,0x0000000100000000)
  to   space 2560K, 0% used [0x00000000ffb00000,0x00000000ffb00000,0x00000000ffd80000)
 ParOldGen       total 44032K, used 41321K [0x00000000fc000000, 0x00000000feb00000, 0x00000000feb00000)
  object space 44032K, 93% used [0x00000000fc000000,0x00000000fe85a6d0,0x00000000feb00000)
 Metaspace       used 3064K, capacity 4496K, committed 4864K, reserved 1056768K
  class space    used 336K, capacity 388K, committed 512K, reserved 1048576K
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at lightbox.GcWIthoutComp.main(GcWIthoutComp.java:11)

According to logs , JVM was not able to allocate memory for string second time ,but if I add JVM arg -Xcomp , it will work and provide this output 根据日志,JVM无法第二次为字符串分配内存,但是如果我添加JVM arg -Xcomp ,它将可以工作并提供此输出

59 free
10485760
19 free
2019-06-09T15:01:06.593+0600: 0.830: [GC (Allocation Failure) [PSYoungGen: 1982K->512K(18944K)] 42942K->41480K(62976K), 0.0008554 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
2019-06-09T15:01:06.594+0600: 0.831: [Full GC (Ergonomics) [PSYoungGen: 512K->0K(18944K)] [ParOldGen: 40968K->411K(37376K)] 41480K->411K(56320K), [Metaspace: 3377K->3377K(1056768K)], 0.0100865 secs] [Times: user=0.01 sys=0.00, real=0.02 secs] 
10485760
20 free
Heap
 PSYoungGen      total 18944K, used 1147K [0x00000000feb00000, 0x0000000100000000, 0x0000000100000000)
  eden space 16384K, 7% used [0x00000000feb00000,0x00000000fec1ed48,0x00000000ffb00000)
  from space 2560K, 0% used [0x00000000ffb00000,0x00000000ffb00000,0x00000000ffd80000)
  to   space 2560K, 0% used [0x00000000ffd80000,0x00000000ffd80000,0x0000000100000000)
 ParOldGen       total 44032K, used 41371K [0x00000000fc000000, 0x00000000feb00000, 0x00000000feb00000)
  object space 44032K, 93% used [0x00000000fc000000,0x00000000fe866c90,0x00000000feb00000)
 Metaspace       used 3399K, capacity 4500K, committed 4864K, reserved 1056768K
  class space    used 334K, capacity 388K, committed 512K, reserved 1048576K

As I understand compiled code is much comfortable for GC in terms of detecting unused memory. 据我了解,就检测未使用的内存而言,编译后的代码对于GC非常适合。 Moreover if I remove -Xcomp and move array creation to separate method it will not throw OOM exception: 此外,如果我删除-Xcomp并将数组创建移动到单独的方法,它将不会引发OOM异常:

public static void main(String[] args) {
        System.out.println(Runtime.getRuntime().freeMemory() / (1024 * 1024) + " free");

        allocate();

        System.out.println(Runtime.getRuntime().freeMemory() / (1024 * 1024) + " free");
        allocate();
        System.out.println(Runtime.getRuntime().freeMemory() / (1024 * 1024) + " free");

    }

    private static void allocate() {
        String[] strings = new String[(40 * 1024 * 1024) / Integer.BYTES];
        System.out.println(strings.length);
    }

My questions are: 我的问题是:

  1. Why does -Xcomp solve problem with allocation as was shown above? 为什么-Xcomp如上所示解决分配问题?

  2. Why does moving allocation to separate method solved my problem? 为什么将分配移动到单独的方法解决了我的问题?

If you have helpful links, please provide them in comments. 如果您有有用的链接,请在评论中提供它们。

The comment is just placed in one line and it is hard to explain something in there. 注释仅放在一行中,很难在其中解释。 The important difference seems to be, that with -Xcomp, the local variable strings is already available to be collected. 重要的区别似乎是,使用-Xcomp,本地变量字符串已经可以被收集。 So you get: 这样就得到:

2019-06-09T15:01:06.593+0600: 0.830: [GC (Allocation Failure) [PSYoungGen: 1982K->512K(18944K)] 42942K->41480K(62976K), 0.0008554 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] 
2019-06-09T15:01:06.594+0600: 0.831: [Full GC (Ergonomics) [PSYoungGen: 512K->0K(18944K)] [ParOldGen: 40968K->411K(37376K)] 41480K->411K(56320K), [Metaspace: 3377K->3377K(1056768K)], 0.0100865 secs] [Times: user=0.01 sys=0.00, real=0.02 secs] 

So you get an Allocation Failure but the GC can free enough memory. 因此您会遇到分配失败,但GC可以释放足够的内存。

Without -Xcomp, the loal variable strings cannot be collected so the message you get is something like: 没有-Xcomp,就无法收集loal变量字符串,因此您得到的消息类似于:

2019-06-09T14:53:18.868+0600: 0.105: [GC (Allocation Failure) [PSYoungGen: 1640K->480K(18944K)] 42600K->41448K(62976K), 0.0018199 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
2019-06-09T14:53:18.870+0600: 0.107: [Full GC (Ergonomics) [PSYoungGen: 480K->0K(18944K)] [ParOldGen: 40968K->41339K(44032K)] 41448K->41339K(62976K), [Metaspace: 3033K->3033K(1056768K)], 0.0174681 secs] [Times: user=0.04 sys=0.00, real=0.02 secs] 
2019-06-09T14:53:18.888+0600: 0.125: [GC (Allocation Failure) [PSYoungGen: 0K->0K(18944K)] 41339K->41339K(62976K), 0.0017270 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
2019-06-09T14:53:18.889+0600: 0.126: [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(18944K)] [ParOldGen: 41339K->41321K(44032K)] 41339K->41321K(62976K), [Metaspace: 3033K->3033K(1056768K)], 0.0140842 secs] [Times: user=0.03 sys=0.01, real=0.01 secs] 

which shows you that the GC tries to free memory but cannot do it. 这表明您GC尝试释放内存,但无法释放内存。

(Look at the PSYoungGen: It shows how the memory comes down. When it failed without -Xcomp, not much was possible. With -Xcomp it gets down from 1982K to 512K. Also see https://dzone.com/articles/understanding-garbage-collection-log ) (查看PSYoungGen:它显示了内存如何减少。如果没有-Xcomp时内存失败,则几乎不可能。使用-Xcomp时,它从1982K下降到512K。另请参见https://dzone.com/articles/understanding -垃圾收集日志

I was unable to find the exact stuff, that -Xcomp is doing. 我无法找到-Xcomp正在执行的确切操作。 So I am sorry that I can only give you the interpretation of the log right now. 因此,很抱歉,我现在只能为您解释日志。 So it is like setting strings to null before assigning a newly generated array. 因此,就像在分配新生成的数组之前将字符串设置为null一样。 So if you add a strings = null; 因此,如果您添加一个字符串= null; before assigning the array a second time: Even without -Xcomp you get more or less the same message of the gc. 在第二次分配数组之前:即使没有-Xcomp,您也会或多或少地得到与gc相同的消息。

And regarding using it inside a method: You have a local variable inside your method. 关于在方法内部使用它:方法内部有一个局部变量。 So you assign the array and then the method ends and the local variable can be collected by the garbage collector. 因此,您分配了数组,然后方法结束,垃圾回收器可以收集局部变量。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM