1. 程式人生 > >Permgen OOM分析簡介(如何獲取String.intern內容)

Permgen OOM分析簡介(如何獲取String.intern內容)

Permgen OOM分析簡介(如何獲取String.intern內容)

一、常用的分析Permgen OOM的方式:

一般出現Permgen的OutOfMemory記憶體溢位,上網搜尋解決方式,一般給出的建議都是增大Permgen的記憶體配置,如-XX:MaxPermSize=128m。

但其實程式本身可能就存在不合理的地方。

Permgen包含2部分資料,一部分是載入的class,一部分是String.intern即常量池快取。

1、 分析載入的class

可以使用MemoryAnalyzer工具擷取Heap,把其中的所有class記錄下來,然後寫一個簡單的載入類程式碼(Class.forname)載入所有記錄的class,然後用jconsole看一下Permgen的大小,即可知道載入的class所佔用的大小。

2、 使用工具jmap –permstat也可以檢視(但必須是jdk1.6.0_31以後的版本)

$ jmap -permstat 543

Attaching to process ID 543, please wait…

Debugger attached successfully.

Server compiler detected.

JVM version is 19.1-b02-334

14584 intern Strings occupying 1603648 bytes.

finding class loader instances ..Warning: skipping invalid TLAB for thread

[email protected]

3、 分析載入的String.intern()常量池的記憶體大小

   工具jmap –permstat可以檢視其大小,但如果要看String的具體內容,可以用jdk安裝目錄/lib/sa-jdi.jar(Serviceability Agent)來檢視。jstack、jmap等工具在使用-F引數啟動時其實就是通過SA來實現功能的。

二、SA簡介:

1、HotSpot有一套私有API提供了對JVM內部資料結構的審視功能,稱為Serviceability Agent。它是一套Java API,雖然HotSpot是用C++寫的,但SA提供了HotSpot中重要資料結構的Java映象類,所以可以直接寫Java程式碼來檢視一個跑在HotSpot上的Java程序的內部狀態。它也提供了一些封裝好的工具,可以直接在命令列上跑。
SA的一個重要特徵是它是“程序外審視工具”。也就是說,SA並不執行在要審視的目標程序中,而是執行在一個獨立的Java程序中,通過作業系統上提供的除錯API來連線到目標程序上。這樣,SA的執行不會受到目標程序狀態的影響,因而可以用於審視一個已經掛起的Java程序,或者是core dump檔案。當然,這也就意味這一個SA程序不能用於審視自己。
一個被偵錯程式連線上的程序會被暫停下來。所以在SA連線到目標程序時,目標程序也是一直處於暫停狀態的,直到SA解除連線。如果需要在線上使用SA的話需要小心,不要通過SA做過於耗時的分析,寧可先把資料都抓出來,把SA的連線解除掉之後再離線分析。

2、使用方式:

java -classpath .;..\lib\sa-jdi.jar demo.PrintStringTable <PID>

3、java -cp %JAVA_HOME%\lib\sa-jdi.jar sun.jvm.hotspot.HSDB

可以開啟一個介面attach到程序

4CLHSDB使用

F:\DevelopTool\Java\jdk1.7.0_07\bin>java -classpath .;F:\DevelopTool\Java\jdk1.7.0_07\lib\sa-jdi.jar sun.jvm.hotspot.CLHSDB

hsdb> help

Available commands:

  assert true | false

  attach pid | exec core

  class name

  classes

  detach

  dis address [ length ]

  dumpcfg { -a | id }

  dumpclass { address | name } [ directory ]

  dumpcodecache

  dumpheap [ file ]

  dumpideal { -a | id }

  dumpilt { -a | id }

  echo [ true | false ]

  examine [ address/count ] | [ address,address]

  field [ type [ name fieldtype isStatic offset address ] ]

  findpc address

  flags [ flag | -nd ]

  help [ command ]

  history

  inspect expression

  intConstant [ name [ value ] ]

  jdis address

  jhisto

  jseval script

  jsload file

  jstack [-v]

  livenmethods

  longConstant [ name [ value ] ]

  mem address [ length ]

  pmap

  print expression

  printas type expression

  printmdo [ -a | expression ]

  printstatics [ type ]

  pstack [-v]

  quit

  reattach

  revptrs address

  scanoops start end [ type ]

  search [ heap | perm | rawheap | codecache | threads ] value

  source filename

  symbol address

  symboldump

  symboltable name

  sysprops

  thread { -a | id }

  threads

  tokenize …

  type [ type [ name super isOop isInteger isUnsigned size ] ]

  universe

  verbose true | false

  versioncheck [ true | false ]

  vmstructsdump

  whatis address

  where { -a | id }

hsdb>

其中常用的有classes檢視程序所有載入的類,flags檢視程序的所有的JVM引數,sysprops檢視程序的所有的系統屬性,universe檢視程序記憶體使用

5、jconsole jstack jmap jinfo用到了Dynamic Attach

jinfo(-flags and -sysprops), jmap(-F –permstat), jstack(-F)用到了SA

6、但JDK6在windows上用SA會丟擲異常:

Attaching to process ID 8980, please wait…

Error attaching to process:

Timed out while attempting to connect to debug server (please start SwDbgSrv.exe).

目前(JDK6)在Windows上SA沒有隨HotSpot一起釋出,所以無法在Windows上使用;在Linux、Solaris、Mac上使用都沒問題。從JDK7 build 64開始Windows版JDK也帶上SA。

三、獲取Permgen中的String.intern的內容:

1、使用以下程式碼獲取PermgenString常量池內容

public class PrintStringTable extends Tool {

    public PrintStringTable() {

    }

    class StringPrinter implements StringTable.StringVisitor {

        private OopField stringValueField;

        public StringPrinter() {

            VM vm = VM.getVM();

            SystemDictionary sysDict = vm.getSystemDictionary();

            InstanceKlass strKlass = sysDict.getStringKlass();

            stringValueField = (OopField) strKlass.findField(“value”, “[C”);

        }

        @Override

        public void visit(Instance instance) {

            TypeArray charArray = ((TypeArray)stringValueField.getValue(instance));

            StringBuilder sb = new StringBuilder();

            for(long i=0;i<charArray.getLength();i++) {

                sb.append(charArray.getCharAt(i));

            }

            System.out.println(“Address: ” + instance.getHandle() + ” Content: ” + sb.toString());

            //System.out.println(sb.toString());

        }

    }

    public static void main(String args[]) throws Exception {

        if(args.length == 0 || args.length > 1) {

        System.err.println(“Usage: PrintStringTable <PID of the JVM whose string table you want to print>”);

        System.exit(1);

        }

        PrintStringTable pst = new PrintStringTable();

        pst.start(args);

        pst.stop();

    }

    @Override

    public void run() {

        StringTable table = VM.getVM().getStringTable();

        table.stringsDo(new StringPrinter());

    }

}

2、例子程式:

package demo;

public class TestStrIntern

{

    static String str = “staticstr”;

    String str1;

    private String createString()

    {

        char[] array = new char[10];

        for(int i=9;i>=0;i–)

        {

            array[i] = (char)i;

        }

//        new String(array);

//        return new String(“9876543210”);

        return new StringBuilder().append(“abc12abc”).append(“223cba223”).toString();

    }

    public static void main(String[] args)

    {

        TestStrIntern obj = new TestStrIntern();

        obj.str1 = “nonintern11122334455”;

        new String(“123123123”).intern();

        new String(“tmp3312334”).intern();

        new String(“1231241234124324231446536567”).intern();

        new String(“nonintern123421”);

        new String(“nonintern122345546”);

        String str2 = obj.createString();

        System.out.println(str2);

        try

        {

            Thread.sleep(30*60*1000);

        }

        catch (InterruptedException e)

        {

            e.printStackTrace();

        }

    }

}

3、執行java -classpath .;..\lib\sa-jdi.jar demo.PrintStringTable <PID>,將會列印abc12abc和223cba223,但不會列印abc12abc223cba223:

ddress: 0x22cc0260 Content: abc12abc

Address: 0x37b99860 Content: ([Ljava/lang/String;)V

Address: 0x37b9d070 Content: ISO_8859_13

Address: 0x37ba0f70 Content: dispatchMouseWheelToAncestor

Address: 0x37ba3bc8 Content: Could not access Graphics Environment:

Address: 0x37ba7ff0 Content: ScaledBlit(…)

Address: 0x37badbd0 Content:  font-style: italic ;

Address: 0x37bb3018 Content: MenuBar.background

Address: 0x37bb6138 Content: SplitPane.rightButton.textAndMnemonic

Address: 0x22cc0298 Content: 223cba223

Address: 0x22b63f38 Content: IBM949C

Address: 0x37b9a598 Content: char

Address: 0x37b9b1f8 Content: 437

Address: 0x37ba5c68 Content: ,keyChar=

Address: 0x37baf5e0 Content: List.focusInputMap