10
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Javaのオブジェクト生成とメモリ管理について

Posted at

#Javaのオブジェクト生成とメモリ管理について

社会人1年目のSEです。
文系出身で分からないなりに日々Javaについて勉強しています。

先日、OOMEが出た時に初めてJVMのメモリ管理について意識するようになりました。
原因はループ内でのオブジェクト大量生成だったのですが、オブジェクトの大量生成がどれほど負荷がかかるのか半信半疑だったので、検証してみます。

##検証方法
百万回ループさせる中で、StringBuilderを新しく作るか使いまわすかで同じ処理をさせ、JVM引数に
-verbose:gc
-XX:+PrintGCDetails
を指定して、GCとヒープ領域の様子を見ます。

1.オブジェクトをループ毎にインスタンス化する場合

    public static void main(String[] args) {
        long start = System.currentTimeMillis();

        for (long i = 1; i < 1000000; i++) {
            StringBuilder stash = new StringBuilder();
            stash.append("text1");
            stash.append("text2");
            stash.append("text3");
            stash.append("text4");
            stash.append("text5");
        }

        long end = System.currentTimeMillis();
        System.out.println((end - start) + "ms");
    }

結果

[GC [PSYoungGen: 16384K->533K(18944K)] 16384K->533K(60928K), 0.0024591 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 16917K->485K(18944K)] 16917K->485K(60928K), 0.0018063 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 16869K->485K(18944K)] 16869K->485K(60928K), 0.0019565 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 16869K->501K(35328K)] 16869K->501K(77312K), 0.0014947 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 33269K->517K(35328K)] 33269K->517K(77312K), 0.0015976 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 33285K->501K(66560K)] 33285K->501K(108544K), 0.0016127 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
209ms
Heap
 PSYoungGen      total 66560K, used 29444K [0x00000000eb600000, 0x00000000ef800000, 0x0000000100000000)
  eden space 65536K, 44% used [0x00000000eb600000,0x00000000ed243a50,0x00000000ef600000)
  from space 1024K, 48% used [0x00000000ef700000,0x00000000ef77d630,0x00000000ef800000)
  to   space 1024K, 0% used [0x00000000ef600000,0x00000000ef600000,0x00000000ef700000)
 ParOldGen       total 41984K, used 0K [0x00000000c2200000, 0x00000000c4b00000, 0x00000000eb600000)
  object space 41984K, 0% used [0x00000000c2200000,0x00000000c2200000,0x00000000c4b00000)
 PSPermGen       total 21504K, used 2618K [0x00000000bd000000, 0x00000000be500000, 0x00000000c2200000)
  object space 21504K, 12% used [0x00000000bd000000,0x00000000bd28ebd8,0x00000000be500000)

2.一つのオブジェクトを使いまわす場合

    public static void main(String[] args) {
        long start = System.currentTimeMillis();

        StringBuilder stash = new StringBuilder();
        for (long i = 1; i < 1000000; i++) {
            stash.append("text1");
            stash.append("text2");
            stash.append("text3");
            stash.append("text4");
            stash.append("text5");
            stash.setLength(0);
        }

        long end = System.currentTimeMillis();
        System.out.println((end - start) + "ms");
    }

結果

60ms
Heap
 PSYoungGen      total 18944K, used 1359K [0x00000000eb600000, 0x00000000ecb00000, 0x0000000100000000)
  eden space 16384K, 8% used [0x00000000eb600000,0x00000000eb753c70,0x00000000ec600000)
  from space 2560K, 0% used [0x00000000ec880000,0x00000000ec880000,0x00000000ecb00000)
  to   space 2560K, 0% used [0x00000000ec600000,0x00000000ec600000,0x00000000ec880000)
 ParOldGen       total 41984K, used 0K [0x00000000c2200000, 0x00000000c4b00000, 0x00000000eb600000)
  object space 41984K, 0% used [0x00000000c2200000,0x00000000c2200000,0x00000000c4b00000)
 PSPermGen       total 21504K, used 2616K [0x00000000bd000000, 0x00000000be500000, 0x00000000c2200000)
  object space 21504K, 12% used [0x00000000bd000000,0x00000000bd28e0f8,0x00000000be500000)

結果、使いまわしたほうはGCが発生しないし、その分処理も高速である。

##privateメソッドに切り出すと?

メソッドを切り出すと、参照喪失のタイミングがループブロック単位ではなくメソッドのブロック単位になる。
が、メモリ効率的には毎回インスタンスしているのと変わらないはず。


    public static void main(String[] args) {

        long start = System.currentTimeMillis();

        for (long i = 1; i < 1000000; i++) {
            makeSentence();
        }

        long end = System.currentTimeMillis();
        System.out.println((end - start) + "ms");
    }

    public static void makeSentence() {
        StringBuilder stash = new StringBuilder();
        stash.append("text1");
        stash.append("text2");
        stash.append("text3");
        stash.append("text4");
        stash.append("text5");
    }

結果


[GC [PSYoungGen: 16384K->517K(18944K)] 16384K->517K(60928K), 0.0020680 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 16901K->592K(18944K)] 16901K->592K(60928K), 0.0022169 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 16976K->517K(18944K)] 16976K->517K(60928K), 0.0014909 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 16901K->453K(35328K)] 16901K->453K(77312K), 0.0020386 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 33221K->501K(35328K)] 33221K->501K(77312K), 0.0015145 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC [PSYoungGen: 33269K->453K(66560K)] 33269K->453K(108544K), 0.0013017 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
202ms
Heap
 PSYoungGen      total 66560K, used 29396K [0x00000000eb600000, 0x00000000ef800000, 0x0000000100000000)
  eden space 65536K, 44% used [0x00000000eb600000,0x00000000ed243a50,0x00000000ef600000)
  from space 1024K, 44% used [0x00000000ef700000,0x00000000ef771620,0x00000000ef800000)
  to   space 1024K, 0% used [0x00000000ef600000,0x00000000ef600000,0x00000000ef700000)
 ParOldGen       total 41984K, used 0K [0x00000000c2200000, 0x00000000c4b00000, 0x00000000eb600000)
  object space 41984K, 0% used [0x00000000c2200000,0x00000000c2200000,0x00000000c4b00000)
 PSPermGen       total 21504K, used 2619K [0x00000000bd000000, 0x00000000be500000, 0x00000000c2200000)
  object space 21504K, 12% used [0x00000000bd000000,0x00000000bd28ed98,0x00000000be500000)

結果、毎回インスタンスするのとほぼ同じ結果。

##結論?

ループ内ではインスタンス生成をさせることはもちろん、インスタンス生成を含むprivateメソッドの使用も避けたほうが良い?
でもそれでメソッド切り出しをしないのも変な気がする。
参照渡しで実装するのでしょうか?

10
6
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?