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 t@44819 ...

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

转载请注明:运维派 » Permgen OOM分析简介(如何获取String.intern内容)

0
2.9k
0