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到进程
4、CLHSDB使用
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、使用以下代码获取Permgen的String常量池内容
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
本文链接:http://www.yunweipai.com/2233.html