1、JDK命令行工具
1.1、jps命令
jps用于列出Java的进程,jps可以增加参数,-m用于输出传递给Java进程的参数,-l用于输出主函数的完整路径,-v可以用于显示传递给jvm的参数。
|
1 2 |
jps -l -m -v 31427 sun.tools.jps.Jps -l -m -v -Dapplication.home= /Library/Java/JavaVirtualMachines/jdk1.7.0_55.jdk/Contents/Home -Xms8m |
1.2、jstat命令
jstat是一个可以用于观察Java应用程序运行时信息的工具,它的功能非常强大,可以通过它查看堆信息的详细情况,它的基本使用方法为:
|
1 |
jstat -<option> [-t] [-h<lines>] <vmid> [<interval> [<count>]] |
选项option可以由以下值组成:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
jstat -class pid:显示加载class的数量,及所占空间等信息。 jstat -compiler pid:显示VM实时编译的数量等信息。 jstat -gc pid:可以显示gc的信息,查看gc的次数,及时间。其中最后五项,分别 是young gc的次数,young gc的时间,full gc的次数,full gc的时间,gc的总时间。 jstat -gccapacity:可以显示,VM内存中三代(young,old,perm)对象的使用和占用大小,如: PGCMN显示的是最小perm的内存使用量,PGCMX显示的是perm的内存最大使用量,PGC是 当前新生成的perm内存占用量,PC是但前perm内存占用量。其他的可以根据这个类推, OC是old内纯的占用量。 jstat -gcnew pid:new对象的信息。 jstat -gcnewcapacity pid:new对象的信息及其占用量。 jstat -gcold pid:old对象的信息。 jstat -gcoldcapacity pid:old对象的信息及其占用量。 jstat -gcpermcapacity pid: perm对象的信息及其占用量。 jstat -gcutil pid:统计gc信息统计。 jstat -printcompilation pid:当前VM执行的信息。 除了以上一个参数外,还可以同时加上 两个数字,如: jstat -printcompilation 3024 250 6是每250毫秒打印一次,一共打印6次。 |
这些参数中最常用的参数是gcutil,下面是该参数的输出介绍以及一个简单例子:
1.3、jinfo命令
jinfo可以用来查看正在运行的Java应用程序的扩展参数,甚至在运行时修改部分参数,它的基本语法为:
|
1 |
jinfo <option> <pid> |
jinfo可以查看运行时参数:
|
1 2 |
jinfo -flag MaxTenuringThreshold 31518 -XX:MaxTenuringThreshold=15 |
jinfo还可以在运行时修改参数值:
|
1 2 3 4 5 |
> jinfo -flag PrintGCDetails 31518 -XX:-PrintGCDetails > jinfo -flag +PrintGCDetails 31518 > jinfo -flag PrintGCDetails 31518 -XX:+PrintGCDetails |
1.4、jmap命令
jmap命令主要用于生成堆快照文件,它的使用方法如下:
|
1 2 3 |
> jmap -dump:format=b,file=heap.hprof 31531 Dumping heap to /Users/caojie/heap.hprof ... Heap dump file created |
获得堆快照文件之后,我们可以使用多种工具对文件进行分析,例如jhat,visual vm等。
1.5、jhat命令
使用jhat工具可以分析Java应用程序的堆快照文件,使用命令如下:
|
1 2 3 4 5 6 7 8 9 10 |
> jhat heap.hprof Reading from heap.hprof... Dump file created Tue Nov 11 06:02:05 CST 2014 Snapshot read, resolving... Resolving 8781 objects... Chasing references, expect 1 dots. Eliminating duplicate references. Snapshot resolved. Started HTTP server on port 7000 Server is ready. |
jhat在分析完成之后,使用HTTP服务器展示其分析结果,在浏览器中访问http://127.0.0.1:7000/即可得到分析结果。
1.6、jstack命令
jstack可用于导出Java应用程序的线程堆栈信息,语法为:
|
1 |
jstack -l <pid> |
jstack可以检测死锁,下例通过一个简单例子演示jstack检测死锁的功能。java代码如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
public class DeadLock extends Thread { protected Object myDirect; static ReentrantLock south = new ReentrantLock(); static ReentrantLock north = new ReentrantLock();
public DeadLock(Object obj) { this.myDirect = obj; if (myDirect == south) { this.setName("south"); } if (myDirect == north) { this.setName("north"); } }
@Override public void run() { if (myDirect == south) { try { north.lockInterruptibly(); try { Thread.sleep(500); } catch (Exception e) { e.printStackTrace(); } south.lockInterruptibly(); System.out.println("car to south has passed"); } catch (InterruptedException e1) { System.out.println("car to south is killed"); } finally { if (north.isHeldByCurrentThread()) north.unlock(); if (south.isHeldByCurrentThread()) south.unlock(); }
} if (myDirect == north) { try { south.lockInterruptibly(); try { Thread.sleep(500); } catch (Exception e) { e.printStackTrace(); } north.lockInterruptibly(); System.out.println("car to north has passed"); } catch (InterruptedException e1) { System.out.println("car to north is killed"); } finally { if (north.isHeldByCurrentThread()) north.unlock(); if (south.isHeldByCurrentThread()) south.unlock(); }
} }
public static void main(String[] args) throws InterruptedException { DeadLock car2south = new DeadLock(south); DeadLock car2north = new DeadLock(north); car2south.start(); car2north.start(); Thread.sleep(1000); } } |
使用jps命令查看进程号为32627,然后使用jstack -l 32637 > a.txt命令把堆栈信息打印到文件中,该文件内容如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
2014-11-11 21:33:12 Full thread dump Java HotSpot(TM) 64-Bit Server VM (24.55-b03 mixed mode):
"Attach Listener" daemon prio=5 tid=0x00007f8d0c803000 nid=0x3307 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers: - None
"DestroyJavaVM" prio=5 tid=0x00007f8d0b80b000 nid=0x1903 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers: - None
"north" prio=5 tid=0x00007f8d0c075000 nid=0x5103 waiting on condition [0x0000000115b06000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x00000007d55ab600> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186) at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:834) at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:894) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1221) at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:340) at DeadLock.run(DeadLock.java:48)
Locked ownable synchronizers: - <0x00000007d55ab5d0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
"south" prio=5 tid=0x00007f8d0c074800 nid=0x4f03 waiting on condition [0x0000000115a03000] java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x00000007d55ab5d0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186) at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:834) at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:894) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1221) at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:340) at DeadLock.run(DeadLock.java:28)
Locked ownable synchronizers: - <0x00000007d55ab600> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
"Service Thread" daemon prio=5 tid=0x00007f8d0c025800 nid=0x4b03 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers: - None
"C2 CompilerThread1" daemon prio=5 tid=0x00007f8d0c025000 nid=0x4903 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers: - None
"C2 CompilerThread0" daemon prio=5 tid=0x00007f8d0d01b000 nid=0x4703 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers: - None
"Signal Dispatcher" daemon prio=5 tid=0x00007f8d0c022000 nid=0x4503 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers: - None
"Finalizer" daemon prio=5 tid=0x00007f8d0d004000 nid=0x3103 in Object.wait() [0x000000011526a000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x00000007d5505568> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135) - locked <0x00000007d5505568> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151) at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:189)
Locked ownable synchronizers: - None
"Reference Handler" daemon prio=5 tid=0x00007f8d0d001800 nid=0x2f03 in Object.wait() [0x0000000115167000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x00000007d55050f0> (a java.lang.ref.Reference$Lock) at java.lang.Object.wait(Object.java:503) at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133) - locked <0x00000007d55050f0> (a java.lang.ref.Reference$Lock)
Locked ownable synchronizers: - None
"VM Thread" prio=5 tid=0x00007f8d0b83b000 nid=0x2d03 runnable
"GC task thread#0 (ParallelGC)" prio=5 tid=0x00007f8d0b818000 nid=0x2503 runnable
"GC task thread#1 (ParallelGC)" prio=5 tid=0x00007f8d0b819000 nid=0x2703 runnable
"GC task thread#2 (ParallelGC)" prio=5 tid=0x00007f8d0d000000 nid=0x2903 runnable
"GC task thread#3 (ParallelGC)" prio=5 tid=0x00007f8d0d001000 nid=0x2b03 runnable
"VM Periodic Task Thread" prio=5 tid=0x00007f8d0c02e800 nid=0x4d03 waiting on condition
JNI global references: 109
Found one Java-level deadlock: ============================= "north": waiting for ownable synchronizer 0x00000007d55ab600, (a java.util.concurrent.locks.ReentrantLock$NonfairSync), which is held by "south" "south": waiting for ownable synchronizer 0x00000007d55ab5d0, (a java.util.concurrent.locks.ReentrantLock$NonfairSync), which is held by "north"
Java stack information for the threads listed above: =================================================== "north": at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x00000007d55ab600> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186) at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:834) at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:894) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1221) at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:340) at DeadLock.run(DeadLock.java:48) "south": at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x00000007d55ab5d0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186) at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:834) at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:894) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1221) at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:340) at DeadLock.run(DeadLock.java:28)
Found 1 deadlock. |
从这个输出可以知道:
1、在输出的最后一段,有明确的"Found one Java-level deadlock"输出,所以通过jstack命令我们可以检测死锁;
2、输出中包含了所有线程,除了我们的north,sorth线程外,还有"Attach Listener", "C2 CompilerThread0", "C2 CompilerThread1"等等;
3、每个线程下面都会输出当前状态,以及这个线程当前持有锁以及等待锁,当持有与等待造成循环等待时,将导致死锁。
1.7、jstatd命令
jstatd命令是一个RMI服务器程序,它的作用相当于代理服务器,建立本地计算机与远程监控工具的通信,jstatd服务器能够将本机的Java应用程序信息传递到远程计算机,由于需要多台计算机做演示,此处略。
1.8、hprof工具
hprof工具可以用于监控Java应用程序在运行时的CPU信息和堆信息,关于hprof的官方文档如下:https://docs.oracle.com/javase/7/docs/technotes/samples/hprof.html
2、Visual VM工具
Visual VM是一个功能强大的多合一故障诊断和性能监控的可视化工具,它集成了多种性能统计工具的功能,使用Visual VM可以替代jstat、jmap、jhat、jstack等工具。在命令行输入jvisualvm即可启动visualvm。
打开Visual VM之后,左边导航栏会显示出当前机器所有Java进程:
点击你想监控的程序即可对该程序进行监控,Visual VM的性能监控页一共有以下几个tab页:
概述页会显示程序的基本使用情况,比如,进程ID,系统属性,启动参数等。
通过监视页面,可以监视应用程序的CPU、堆、永久区、类加载器和线程数的整体情况,通过页面上的Perform GC和Heap Dump按钮还可以手动执行Full GC和生成堆快照。
线程页面会提供详细的线程信息,单击Thread Dump按钮可以导出当前所有线程的堆栈信息,如果Visual VM在当前线程中找到死锁,则会以十分显眼的方式在Threads页面给予提示。
抽样器可以对CPU和内存两个性能进行抽样,用于实时地监控程序。CPU采样器可以将CPU占用时间定位到方法,内存采样器可以查看当前程序的堆信息。下面是一个频繁调用的Java程序,我们会对改程序进行采样:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
public class MethodTime { static java.util.Random r=new java.util.Random(); static Map<String,String> map=null; static{ map=new HashMap<String,String>(); map.put("1", "Java"); map.put("2", "C++"); map.put("3", "Delphi"); map.put("4", "C"); map.put("5", "Phython"); } public String getNameById(String id){ try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } return map.get(id); }
public List<String> getNamesByIds(String ids){ List<String> re=new ArrayList<String>(); String[] strs=ids.split(","); for(String id:strs){ re.add(getNameById(id)); } return re; }
public List<String> getNamesByIdsBad(String ids){ List<String> re=new ArrayList<String>(); String[] strs=ids.split(","); for(String id:strs){ //A bad code getNameById(id); re.add(getNameById(id)); } return re; }
public class NamesByIdsThread implements Runnable{ @Override public void run() { try{ while(true){ int c=r.nextInt(4); String ids=""; for(int i=0;i<c;i++) ids=Integer.toString((r.nextInt(4)+1))+","; getNamesByIds(ids); } }catch(Exception e){ } } }
public class NamesByIdsBadThread implements Runnable{ @Override public void run() { try{ while(true){ int c=r.nextInt(4); String ids=""; for(int i=0;i<c;i++) ids=Integer.toString((r.nextInt(4)+1))+","; getNamesByIdsBad(ids); } }catch(Exception e){ } } }
public static void main(String args[]){ MethodTime instance=new MethodTime(); new Thread(instance.new NamesByIdsThread()).start(); new Thread(instance.new NamesByIdsBadThread()).start(); } } |
通过Visual VM的采样功能,可以找到改程序中占用CPU时间最长的方法:
默认Visual VM不统计内置对象的函数调用,比如java.*包中的类,如果要统计这些内置对象,单机右上角的设置进行调配。Visual VM虽然可以统计方法的调用时间,但是无法给出方法调用堆栈,Jprofile不仅可以给出方法调用时间,还可以给出方法调用堆栈,较Visual VM更强大。
右击左导航的应用程序,会出现以下菜单:
单机应用程序快照,可以分析当前应用程序的快照,单击堆Dump能够对当前的堆信息进行分析。Visual VM的更多使用方法,可以查看Oracle的官方文档https://docs.oracle.com/javase/7/docs/technotes/guides/visualvm/index.html
BTrace插件
BTrace是一款功能强大的性能检测工具,它可以在不停机的情况下,通过字节码注入,动态监控系统的运行情况,它可以跟踪指定的方法调用、构造函数调用和系统内存等信息,本部分打算举一个例子,讲解一下BTrace的使用。要在Visual VM中使用Btrace,首先需要安装Btrace插件,点击工具->插件即可在线安装,安装后右键应用程序,就会出现如下选项:
点击Trace application,即可进入BTrace插件界面。使用BTrace可以监控指定函数的耗时,以下脚本通过正则表达式,监控所有类的getNameById方法:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import com.sun.btrace.annotations.*; import static com.sun.btrace.BTraceUtils.*;
@BTrace public class TracingScript { @TLS private static long startTime = 0;
@OnMethod(clazz="/.+/", method="/getNameById/")//监控任意类的getNameById方法 public static void startMethod() { startTime=timeMillis(); }
@OnMethod(clazz="/.+/", method="/getNameById/", location=@Location(Kind.RETURN))//方法返回时触发 public static void endMethod() { print(strcat(strcat(name(probeClass()), "."), probeMethod())); print(" ["); print(strcat("Time taken : ", str(timeMillis() - startTime))); println("]"); } } |
点击运行,部分输出如下:
|
1 2 3 4 |
MethodTime.getNameById [Time taken : 5] MethodTime.getNameById [Time taken : 4] MethodTime.getNameById [Time taken : 7] MethodTime.getNameById [Time taken : 7] |
BTrace除了可以监控函数耗时外,还可以指定程序运行到某一行代码触发某一行为,定时触发行为,监控函数参数等等。
3、MAT内存分析工具
MAT是一款功能强大的Java堆内存分析器,可以用于查找内存泄露以及查看内存消耗情况,MAT的官方文档如下:http://help.eclipse.org/luna/index.jsp?topic=/org.eclipse.mat.ui.help/welcome.html。
在MAT中有浅堆和深堆的概念,浅堆是指一个对象结构所占用的内存大小,深堆是指一个对象被GC回收后可以真正释放的内存大小。
通过MAT,可以列出所有垃圾回收的根对象,Java系统的根对象可能是以下类:系统类,线程,Java局部变量,本地栈等等。在MAT中还可以很清楚的看到根对象到当前对象的引用关系链。
MAT还可以自动检测内存泄露,单击菜单上的Leak Suspects命令,MAT会自动生成一份报告,这份报告罗列了系统内可能存在内存泄露的问题点。
在MAT中,还可以自动查找并显示消耗内存最多的几个对象,这些消耗大量内存的大对象往往是解决系统性能问题的关键所在。
具体例子,略,网速太慢,至今还未下好。。
4、jvm中的年轻代 老年代 持久代 gc
篇一:
虚拟机中的共划分为三个代:年轻代(Young Generation)、老年代(Old Generation)和持久代(Permanent Generation)。其中持久代主要存放的是Java类的类信息,与垃圾收集要收集的Java对象关系不大。年轻代和年老代的划分是对垃圾收集影响比较大的。
年轻代:
所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。年轻代分三个区。一个Eden区,两个Survivor区(一般而言)。大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当这个Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当这个Survivor去也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制“年老区(Tenured)”。需要注意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从Eden复制过来 对象,和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor去过来的对象。而且,Survivor区总有一个是空的。同时,根据程序需要,Survivor区是可以配置为多个的(多于两个),这样可以增加对象在年轻代中的存在时间,减少被放到年老代的可能。
年老代:
在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。
持久代:
用于存放静态文件,如今Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如Hibernate等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过-XX:MaxPermSize=<N>进行设置。
Scavenge GC
一般情况下,当新对象生成,并且在Eden申请空间失败时,就会触发Scavenge GC,对Eden区域进行GC,清除非存活对象,并且把尚且存活的对象移动到Survivor区。然后整理Survivor的两个区。这种方式的GC是对年轻代的Eden区进行,不会影响到年老代。因为大部分对象都是从Eden区开始的,同时Eden区不会分配的很大,所以Eden区的GC会频繁进行。因而,一般在这里需要使用速度快、效率高的算法,使Eden去能尽快空闲出来。
Full GC
对整个堆进行整理,包括Young、Tenured和Perm。Full GC因为需要对整个对进行回收,所以比Scavenge GC要慢,因此应该尽可能减少Full GC的次数。在对JVM调优的过程中,很大一部分工作就是对于FullGC的调节。有如下原因可能导致Full GC:
· 年老代(Tenured)被写满
· 持久代(Perm)被写满
· System.gc()被显示调用
·上一次GC之后Heap的各域分配策略动态变化
看一段日志:
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask
(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run
(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:662)
Exception in thread "http-bio-17788-exec-74"
java.lang.OutOfMemoryError: PermGen space
Exception in thread "http-bio-17788-exec-75"
java.lang.OutOfMemoryError: PermGen space
Exception in thread "http-bio-17788-exec-76"
java.lang.OutOfMemoryError: PermGen space
明显可以看出是老年代的内存溢出,说明在容器下的静态文件过多,比如编译的字节码,jsp编译成servlet,或者jar包。
解决此问题,修改jvm的参数 permsize即可,permsize初始默认为64m。
篇二:
关键字约定
- Young generation –>新生代
- Tenured / Old Generation –>老年代
- Perm Area –>永久代
年轻代:
所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。年轻代分三个区。一个Eden区,两个 Survivor区(一般而言)。大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当这个 Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当这个Survivor去也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制“年老区(Tenured)”。需要注意,Survivor的两个区是对称的,没先后关系,所以同一个区中可能同时存在从Eden复制过来对象,和从前一个Survivor复制过来的对象,而复制到年老区的只有从第一个Survivor去过来的对象。而且,Survivor区总有一个是空的。同时,根据程序需要,Survivor区是可以配置为多个的(多于两个),这样可以增加对象在年轻代中的存在时间,减少被放到年老代的可能。
年老代:
在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。
持久代:
用于存放静态文件,如今Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,例如hibernate 等,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。持久代大小通过-XX:MaxPermSize=<N>进行设置。
重要的东东
- 在Java中,对象实例都是在堆上创建。一些类信息,常量,静态变量等存储在方法区。堆和方法区都是线程共享的。
- GC机制是由JVM提供,用来清理需要清除的对象,回收堆内存。
- GC机制将Java程序员从内存管理中解放了出来,可以更关注于业务逻辑。
- 在Java中,GC是由一个被称为垃圾回收器的守护线程执行的。
- 在从内存回收一个对象之前会调用对象的finalize()方法。
- 作为一个Java开发者不能强制JVM执行GC;GC的触发由JVM依据堆内存的大小来决定。
- System.gc()和Runtime.gc()会向JVM发送执行GC的请求,但是JVM不保证一定会执行GC。
- 如果堆没有内存创建新的对象了,会抛出
OutOfMemoryError。
GC针对什么对象?
了解GC机制的第一步就是理解什么样的对象会被回收。当一个对象通过一系列根对象(比如:静态属性引用的常量)都不可达时就会被回收。简而言之,当一个对象的所有引用都为null。循环依赖不算做引用,如果对象A有一个指向对象B的引用,对象B也有一个指向对象A的引用,除此之外,它们没有其他引用,那么对象A和对象B都、需要被回收(如下图,ObjA和ObjB需要被回收)。
GC回收对象
堆内存是如何划分的?
Java中对象都在堆上创建。为了GC,堆内存分为三个部分,也可以说三代,分别称为新生代,老年代和永久代。其中新生代又进一步分为Eden区,Survivor 1区和Survivor 2区(如下图)。新创建的对象会分配在Eden区,在经历一次Minor
GC后会被移到Survivor 1区,再经历一次Minor GC后会被移到Survivor 2区,直到升至老年代,需要注意的是,一些大对象(长字符串或数组)可能会直接存放到老年代。
堆内存
永久代有一些特殊,它用来存储类的元信息。对于GC是否发生在永久代有许多不同的看法,在我看来这取决于采用的JVM。大家可以通过创建大量的字符串来观察是发生了GC还是抛出了OutOfMemoryError。
GC算法
- 标记清除算法
分为标记和清除两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。该算法的缺点是效率不高并且会产生不连续的内存碎片。
image - 复制算法
把内存空间划为两个区域,每次只使用其中一个区域。垃圾回收时,遍历当前使用区域,把正在使用中的对象复制到另外一个区域中。次算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去以后还能进行相应的内存整理,不会出现“碎片”问题。优点:实现简单,运行高效。缺点:会浪费一定的内存。一般新生代采用这种算法。
image - 标记整理算法
标记阶段与标记清除算法一样。但后续并不是直接对可回收的对象进行清理,而是让所有存活对象都想一端移动,然后清理。优点是不会造成内存碎片。
image
Java中垃圾回收器的类型
Java提供多种类型的垃圾回收器。JVM中的垃圾收集一般都采用“分代收集”,不同的堆内存区域采用不同的收集算法,主要目的就是为了增加吞吐量或降低停顿时间。
- Serial收集器:新生代收集器,使用复制算法,使用一个线程进行GC,串行,其它工作线程暂停。
- ParNew收集器:新生代收集器,使用复制算法,Serial收集器的多线程版,用多个线程进行GC,并行,其它工作线程暂停。使用-XX:+UseParNewGC开关来控制使用ParNew+Serial Old收集器组合收集内存;使用-XX:ParallelGCThreads来设置执行内存回收的线程数。
- Parallel Scavenge 收集器:吞吐量优先的垃圾回收器,作用在新生代,使用复制算法,关注CPU吞吐量,即运行用户代码的时间/总时间。使用-XX:+UseParallelGC开关控制使用Parallel Scavenge+Serial Old收集器组合回收垃圾。
- Serial Old收集器:老年代收集器,单线程收集器,串行,使用标记整理算法,使用单线程进行GC,其它工作线程暂停。
- Parallel Old收集器:吞吐量优先的垃圾回收器,作用在老年代,多线程,并行,多线程机制与Parallel Scavenge差不错,使用标记整理算法,在Parallel Old执行时,仍然需要暂停其它线程。
- CMS(Concurrent Mark Sweep)收集器:老年代收集器,致力于获取最短回收停顿时间(即缩短垃圾回收的时间),使用标记清除算法,多线程,优点是并发收集(用户线程可以和GC线程同时工作),停顿小。使用-XX:+UseConcMarkSweepGC进行ParNew+CMS+Serial Old进行内存回收,优先使用ParNew+CMS(原因见Full GC和并发垃圾回收一节),当用户线程内存不足时,采用备用方案Serial Old收集。
可以看Java Performance一书来获取更多关于GC调优的信息。
与GC有关的JVM参数
做GC调优需要大量的实践,耐心和对项目的分析。我曾经参与过高容量,低延迟的电商系统,在开发中我们需要通过分析造成Full GC的原因来提高系统性能,在这个过程中我发现做GC的调优很大程度上依赖于对系统的分析,系统拥有怎样的对象以及他们的平均生命周期。
举个例子,如果一个应用大多是短生命周期的对象,那么应该确保Eden区足够大,这样可以减少Minor GC的次数。可以通过-XX:NewRatio来控制新生代和老年代的比例,比如-XX:NewRatio=3代表新生代和老年代的比例为1:3。需要注意的是,扩大新生代的大小会减少老年代的大小,这会导致Major GC执行的更频繁,而Major
GC可能会造成用户线程的停顿从而降低系统吞吐量。JVM中可以用NewSize和MaxNewSize参数来指定新生代内存最小和最大值,如果两个参数值一样,那么就相当于固定了新生代的大小。
个人建议,在做GC调优之前最好深入理解Java中GC机制,推荐阅读Sun
Microsystems提供的有关GC的文档。这个链接可能会对理解GC机制提供一些帮助。下面的图列出了各个区可用的一些JVM参数。
jvm参数
Full GC和并发垃圾回收
并发垃圾回收器的内存回收过程是与用户线程一起并发执行的。通常情况下,并发垃圾回收器可以在用户线程运行的情况下完成大部分的回收工作,所以应用停顿时间很短。
但由于并发垃圾回收时用户线程还在运行,所以会有新的垃圾不断产生。作为担保,如果在老年代内存都被占用之前,如果并发垃圾回收器还没结束工作,那么应用会暂停,在所有用户线程停止的情况下完成回收。这种情况称作Full GC,这意味着需要调整有关并发回收的参数了。
由于Full
GC很影响应用的性能,要尽量避免或减少。特别是如果对于高容量低延迟的电商系统,要尽量避免在交易时间段发生Full GC。
总结
- 为了分代垃圾回收,Java堆内存分为3代:新生代,老年代和永久代。
- 新的对象实例会优先分配在新生代,在经历几次Minor GC后(默认15次),还存活的会被移至老年代(某些大对象会直接在老年代分配)。
- 永久代是否执行GC,取决于采用的JVM。
- Minor GC发生在新生代,当Eden区没有足够空间时,会发起一次Minor GC,将Eden区中的存活对象移至Survivor区。Major GC发生在老年代,当升到老年代的对象大于老年代剩余空间时会发生Major GC。
- 发生Major GC时用户线程会暂停,会降低系统性能和吞吐量。
- JVM的参数-Xmx和-Xms用来设置Java堆内存的初始大小和最大值。依据个人经验这个值的比例最好是1:1或者1:1.5。比如,你可以将-Xmx和-Xms都设为1GB,或者-Xmx和-Xms设为1.2GB和1.8GB。
- Java中不能手动触发GC,但可以用不同的引用类来辅助垃圾回收器工作(比如:弱引用或软引用)。
以上就是关于Java中GC的一些内容。通过这篇博客,我们可以知道堆内存是如何划分的;一个对象在没有任何强引用指向他或该对象通过根节点不可达时需要被垃圾回收器回收;当垃圾收集器意识到需要进行GC时会触发Minor GC或Major GC,是自动的,无法强制执行。
5、java JVM虚拟机选项 Xms Xmx PermSize MaxPermSize 区别
1.参数的含义
-vmargs -Xms128M -Xmx512M -XX:PermSize=64M
-XX:MaxPermSize=128M
-vmargs 说明后面是VM的参数,所以后面的其实都是JVM的参数了
-Xms128m JVM初始分配的堆内存
-Xmx512m JVM最大允许分配的堆内存,按需分配
-XX:PermSize=64M JVM初始分配的非堆内存
-XX:MaxPermSize=128M JVM最大允许分配的非堆内存,按需分配
-Xss每个线程的Stack大小,Stack的大小限制着线程的数量.如果Stack过大就好导致内存溢漏.-Xss参数决定Stack大小,例如-Xss1024K.如果Stack太小,也会导致Stack溢漏。
我们首先了解一下JVM内存管理的机制,然后再解释每个参数代表的含义。
1)堆(Heap)和非堆(Non-heap)内存
按照官方的说法:“Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配。堆是在 Java 虚拟机启动时创建的。”“在JVM中堆之外的内存称为非堆内存(Non-heap memory)”。
可以看出JVM主要管理两种类型的内存:堆和非堆。简单来说堆就是Java代码可及的内存,是留给开发人员使用的;非堆就是JVM留给自己用的,
所以方法区、JVM内部处理或优化所需的内存(如JIT编译后的代码缓存)、每个类结构(如运行时常数池、字段和方法数据)以及方法和构造方法的代码都在非堆内存中。
堆内存分配
JVM初始分配的堆内存由-Xms指定,默认是物理内存的1/64;JVM最大分配的堆内存由-Xmx指定,默认是物理内存的1/4。默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;
空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制。因此服务器一般设置-Xms、-Xmx 相等以避免在每次GC 后调整堆的大小。
说明:如果-Xmx 不指定或者指定偏小,应用可能会导致java.lang.OutOfMemory错误,此错误来自JVM,不是Throwable的,无法用try...catch捕捉。
非堆内存分配
JVM使用-XX:PermSize设置非堆内存初始值,默认是物理内存的1/64;由XX:MaxPermSize设置最大非堆内存的大小,默认是物理内存的1/4。(还有一说:MaxPermSize缺省值和-server -client选项相关,
-server选项下默认MaxPermSize为64m,-client选项下默认MaxPermSize为32m。这个我没有实验。)
上面错误信息中的PermGen space的全称是Permanent Generation space,是指内存的永久保存区域。还没有弄明白PermGen space是属于非堆内存,还是就是非堆内存,但至少是属于了。
XX:MaxPermSize设置过小会导致java.lang.OutOfMemoryError: PermGen space 就是内存益出。
说说为什么会内存益出:
(1)这一部分内存用于存放Class和Meta的信息,Class在被 Load的时候被放入PermGen
space区域,它和存放Instance的Heap区域不同。
(2)GC(Garbage
Collection)不会在主程序运行期对PermGen space进行清理,所以如果你的APP会LOAD很多CLASS 的话,就很可能出现PermGen space错误。
这种错误常见在web服务器对JSP进行pre compile的时候。
2)JVM内存限制(最大值)
1.首先JVM内存限制于实际的最大物理内存,假设物理内存无限大的话,JVM内存的最大值跟操作系统有很大的关系。简单的说就32位处理器虽然可控内存空间有4GB,但是具体的操作系统会给一个限制,
这个限制一般是2GB-3GB(一般来说Windows系统下为1.5G-2G,Linux系统下为2G-3G),而64bit以上的处理器就不会有限制了。
2. 为什么有的机器我将-Xmx和-XX:MaxPermSize都设置为512M之后Eclipse可以启动,而有些机器无法启动?
通过上面对JVM内存管理的介绍我们已经了解到JVM内存包含两种:堆内存和非堆内存,另外JVM最大内存首先取决于实际的物理内存和操作系统。所以说设置VM参数导致程序无法启动主要有以下几种原因:
1) 参数中-Xms的值大于-Xmx,或者-XX:PermSize的值大于-XX:MaxPermSize;
2) -Xmx的值和-XX:MaxPermSize的总和超过了JVM内存的最大限制,比如当前操作系统最大内存限制,或者实际的物理内存等等。说到实际物理内存这里需要说明一点的是,
如果你的内存是1024MB,但实际系统中用到的并不可能是1024MB,因为有一部分被硬件占用了。
3. 为何将上面的参数写入到eclipse.ini文件Eclipse没有执行对应的设置?
那为什么同样的参数在快捷方式或者命令行中有效而在eclipse.ini文件中是无效的呢?这是因为我们没有遵守eclipse.ini文件的设置规则:
参数形如“项 值”这种形式,中间有空格的需要换行书写,如果值中有空格的需要用双引号包括起来。比如我们使用-vm C:/Java/jre1.6.0/bin/javaw.exe参数设置虚拟机,
在eclipse.ini文件中要写成这样:
-vm
C:/Java/jre1.6.0/bin/javaw.exe
-vmargs
-Xms128M
-Xmx512M
-XX:PermSize=64M
-XX:MaxPermSize=128M
实际运行的结果可以通过Eclipse中“Help”-“About
Eclipse SDK”窗口里面的“Configuration Details”按钮进行查看。
另外需要说明的是,Eclipse压缩包中自带的eclipse.ini文件内容是这样的:
-showsplash
org.eclipse.platform
--launcher.XXMaxPermSize
256m
-vmargs
-Xms40m
-Xmx256m
其中–launcher.XXMaxPermSize(注意最前面是两个连接线)跟-XX:MaxPermSize参数的含义基本是一样的,我觉得唯一的区别就是前者是eclipse.exe启动的时候设置的参数,
而后者是eclipse所使用的JVM中的参数。其实二者设置一个就可以了,所以这里可以把–launcher.XXMaxPermSize和下一行使用#注释掉。
4. 其他的启动参数。 如果你有一个双核的CPU,也许可以尝试这个参数:
-XX:+UseParallelGC
让GC可以更快的执行。(只是JDK
5里对GC新增加的参数)
补充:
如果你的WEB
APP下都用了大量的第三方jar,其大小超过了服务器jvm默认的大小,那么就会产生内存益出问题了。
解决方法: 设置MaxPermSize大小
可以在myelipse里选中相应的服务器比如tomcat5,展开里面的JDK子项页面,来增加服务器启动的JVM参数设置:
-Xms128m
-Xmx256m
-XX:PermSize=128M
-XX:MaxNewSize=256m
-XX:MaxPermSize=256m
或者手动设置MaxPermSize大小,比如tomcat,
修改TOMCAT_HOME/bin/catalina.bat,在echo
"Using CATALINA_BASE: $CATALINA_BASE"上面加入以下行:
JAVA_OPTS="-server -XX:PermSize=64M -XX:MaxPermSize=128m
建议:将相同的第三方jar文件移置到tomcat/shared/lib目录下,这样可以减少jar 文档重复占用内存
六、this.getClass().getResourceAsStream()读入数据
JAVA里面对于类进行调用配置资源的文件数据,以this.getClass().getResourceAsStream()来读取比较合适。
路径采用相对路径直接可以从工程的path路径去找。主要问题是如果类中采用的是静态块的话,则该this.getClass()报错,因为读静态块时,可能该对象并为构造,所以用this来指向当前对象不行。
ClassLoader提供了两个方法用于从装载的类路径中取得资源:
public URL
getResource(String name);
public InputStream
getResourceAsStream(String name);
这里name是资源的类路径,它是相对与“/”根路径下的位置。getResource得到的是一个URL对象来定位资源,而getResourceAsStream取得该资源输入流的引用保证程序可以从正确的位置抽取数据。
举例说明:
mypackage.Hello.class.getResourceAsStream("/config/config.ini");
从classpath下的config相对路径中读取config.ini
JAVA里面对于类进行调用配置资源的文件数据,以this.getClass().getResourceAsStream()来读取比较合适。
http://baoqiang2004.blog.163.com/blog/static/16819372920111130110722/
(1).new FileInputStream(“a.txt”)
那么,这个a.txt就应该在工程的根目录下.
(2).this.getClass().getClassLoader().getResourceAsStream(“a.txt”)
在bin目录下,或加载类的地方–包外
(3).this.getClass().getResourceAsStream(“a.txt”)
在bin目录下,或加载类的地方–包内
getResourceAsStream:其本质是,根据类所在的位置,去加载配置文件所在的位置,
http://ouyangfei0426.iteye.com/blog/1020232
http://blog.csdn.net/zhanghaipeng1989/article/details/19332489
Class.getResourceAsStream() 会指定要加载的资源路径与当前类所在包的路径一致。
例如你写了一个MyTest类在包com.test.mycode
下,那么MyTest.class.getResourceAsStream("name")
会在com.test.mycode包下查找相应的资源。
如果这个name是以 '/' 开头的,那么就会从classpath的根路径下开始查找。
1. Class.getResourceAsStream(String path) :
path 不以'/'开头时默认是从此类所在的包下取资源,以'/'开头则是从ClassPath(Src根目录)根下获取。
其只是通过path构造一个绝对路径,最终还是由ClassLoader获取资源。
http://hi.baidu.com/lzpsky/item/6ae30e5dec024c3494eb0526
工程的文件结构:
testjee
src
config
config1.properties
configproperty
ConfigPropManage.java
config2.properties
config
config4.properties
config3.properties
代码:
package configproperty;
public class ConfigPropManage {
private static ConfigPropManage instance = new ConfigPropManage();
private ConfigPropManage() {
}
public static ConfigPropManage getInstance() {
return instance;
}
public static void
main(String[] args) {
ConfigPropManage instance = ConfigPropManage.getInstance();
// 当前包路径, 得到的是当前类class文件的URI目录。不包括自己
//out: file:/D:/workplace-Ec/workspace_jee/testjee/bin/configproperty/
System.out.println(instance.getClass().getResource(""));
// 当前类路径, 得到的是当前的classpath的绝对URI路径
//out: file:/D:/workplace-Ec/workspace_jee/testjee/bin/
System.out.println(instance.getClass().getResource("/"));
////////////////////////////////////////////////config1.properties///////////////////////////////
//不加“/”,则表示从当前类所在的包configproperty下查找该资源config/config1.properties。没找到.
//out: 11:null
System.out.println("11:"+instance.getClass().getResourceAsStream("config/config1.properties"));
//加“/”,则表示从类路径下也就是从classes文件夹下查找资源,找到。
//out: 12:java.io.BufferedInputStream@37a71e93
System.out.println("12:"+instance.getClass().getResourceAsStream("/config/config1.properties"));
//加“/”,则表示从类路径下也就是从classes文件夹下查找资源,没找到。
System.out.println("13:"+instance.getClass().getResourceAsStream("/config1.properties"));
//不加“/”,则表示从当前类所在的包configproperty下查找该资源config1.properties。没找到.
System.out.println("14:"+instance.getClass().getResourceAsStream("config1.properties"));
//加“/”,则表示从类路径下也就是从classes文件夹下查找资源,没找到。
System.out.println("15:"+instance.getClass().getResource("/config1.properties"));
//不加“/”,则表示从当前类所在的包configproperty下查找该资源config1.properties。没找到.
System.out.println("16:"+instance.getClass().getResource("config1.properties"));
//加“/”,则表示从类路径下也就是从classes文件夹下查找资源,没找到。
//out: 17:file:/D:/workplace-Ec/workspace_jee/testjee/bin/config/config1.properties
System.out.println("17:"+instance.getClass().getResource("/config/config1.properties"));
//不加“/”,则表示从当前类所在的包configproperty下查找该资源config/config1.properties。没找到.
System.out.println("18:"+instance.getClass().getResource("config/config1.properties"));
////////////////////////////////////////////////config2.properties//////////////////////////////////
//加“/”,则表示从类路径下也就是从classes文件夹下查找资源,没找到。
System.out.println("23:"+instance.getClass().getResourceAsStream("/config2.properties"));
//文件名前不加“/”,则表示从当前类所在的包configproperty下查找该资源config2.properties。找到.
//out: 24:java.io.BufferedInputStream@7c3df479
System.out.println("24:"+instance.getClass().getResourceAsStream("config2.properties"));
//加“/”,则表示从类路径下也就是从classes文件夹下查找资源,没找到。
System.out.println("25:"+instance.getClass().getResource("/config2.properties"));
//文件名前不加“/”,则表示从当前类所在的包configproperty下查找该资源config2.properties。找到
//out: 26:file:/D:/workplace-Ec/workspace_jee/testjee/bin/configproperty/config2.properties
System.out.println("26:"+instance.getClass().getResource("config2.properties"));
////////////////////////////////////////////////config3.properties/////////////////////////////////
//加“/”,则表示从类路径下也就是从classes文件夹下查找资源config3,找到。
//out: 33:java.io.BufferedInputStream@7106e68e
System.out.println("33:"+instance.getClass().getResourceAsStream("/config3.properties"));
//文件名前不加“/”,则表示从当前类所在的包configproperty下查找该资源config3.properties。没找到
System.out.println("34:"+instance.getClass().getResourceAsStream("config3.properties"));
//加“/”,则表示从类路径下也就是从classes文件夹下查找资源config3,找到。
//out: 35:file:/D:/workplace-Ec/workspace_jee/testjee/bin/config3.properties
System.out.println("35:"+instance.getClass().getResource("/config3.properties"));
//文件名前不加“/”,则表示从当前类所在的包configproperty下查找该资源config3.properties。没找到
System.out.println("36:"+instance.getClass().getResource("config3.properties"));
////////////////////////////////////////////////config4.properties////////////////////////////////////
//不加“/”,则表示从当前类所在的包configproperty下查找该资源config/config4.properties。找到
//out: 41:java.io.BufferedInputStream@7e6cbb7a
System.out.println("41:"+instance.getClass().getResourceAsStream("config/config4.properties"));
//加“/”,则表示从类路径下也就是从classes文件夹下查找资源config/config4.properties,没找到。
System.out.println("42:"+instance.getClass().getResourceAsStream("/config/config4.properties"));
//加“/”,则表示从类路径下也就是从classes文件夹下查找资源config4.properties,没找到。
System.out.println("43:"+instance.getClass().getResourceAsStream("/config4.properties"));
//文件名前不加“/”,则表示从当前类所在的包configproperty下查找该资源config4.properties。没找到
System.out.println("44:"+instance.getClass().getResourceAsStream("config4.properties"));
//加“/”,则表示从类路径下也就是从classes文件夹下查找资源,没找到。
System.out.println("45:"+instance.getClass().getResource("/config4.properties"));
//文件名前不加“/”,则表示从当前类所在的包configproperty下查找该资源config4.properties。没找到
System.out.println("46:"+instance.getClass().getResource("config4.properties"));
//加“/”,则表示从类路径下也就是从classes文件夹下查找资源,没找到。
System.out.println("47:"+instance.getClass().getResource("/config/config4.properties"));
//不加“/”,则表示从当前类所在的包configproperty下查找该资源config/config4.properties。找到
//out: 48:file:/D:/workplace-Ec/workspace_jee/testjee/bin/configproperty/config/config4.properties
System.out.println("48:"+instance.getClass().getResource("config/config4.properties"));
}
}
疑问:
//如果类路径下的当前包有22.properties文件,则输出:6:
file:/E:/myobject/myspider/build/classes/myspider/22.properties
//否者输出源文件下的22.properties文件的路径,则输出:6:file:/E:/myobject/myspider/src/myspider/22.properties
System.out.println("6:"+t.getClass().getResource("22.properties"));
//如果类路径下有11.properties文件,则输出7:file:/E:/myobject/myspider/build/classes/11.properties
//否者输出源文件下的11.properties文件的路径,则输出:7:file:/E:/myobject/myspider/src/11.properties
System.out.println("7:"+t.getClass().getResource("/11.properties"));
来自hadooploader starthadooploader.sh中:
#DEBUG_OPTS="-Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=5005"
#-Dlog.whoami 用户名
#-Dlog.hostname 主机名
#-Xmx 最大堆内存设置为总内存的1/8
#-Xms 初始内存设置为与最大堆内存一致,防止因为内存收缩/突然增大带来的性能影响,避免每次垃圾回收完成后JVM重新分配内存
#-Xmn 设置年轻代大小,这个值比较影响性能,大多数情况下java程序中创建的对象都是从新生代分配内存,如果你的程序需要比较多的临时内存建议增大,Sun官方推荐配置为整个堆的3/8
#-XX:PermSize=128m 设置持久带初始值,从jmap来看,hadooploader会加载比较多的class
#-XX:MaxPermSize=128m 设置持久带最大值
#-XX:+UseParNewGC 设置年轻代为并行收集,可与CMS收集同时使用
#-XX:+UseCMSCompactAtFullCollection 在FULL GC的时候,对年老代的压缩
#-XX:CMSFullGCsBeforeCompaction=5 多少次后进行内存压缩
#-XX:+UseConcMarkSweepGC 使用CMS收集年老代
java -Dlog.whoami=`whoami` -Dlog.hostname=`hostname` -Xmx${MEM_XMX}m -Xms${MEM_XMX}m -Xmn${MEM_XMN}m -XX:PermSize=128m -XX:MaxPermSize=128m -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=5 -DPROC=HadoopLoader -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath="$LOADER_PATH" -classpath "$HADOOP_CLASSPATH" com.zte.hadooploader.HadoopLoader
6. Java虚拟机日志查看工具 gclogviewer
GC的状况是Java程序需要监控的重点,而人肉分析一堆文本自然是相当的痛苦,之前业界主要是用gcviewer来分析,但它不支持jdk6,gclogviewer是一个支持jdk 6的gc log可视化工具,gc log可用-XX:+PrintGCDetails -XX:+PrintGCDateStamps -Xloggc:<gc log文件名>。
GCLogViewer支持:
1、支持根据gc log生成GC的趋势图;
2、生成调优建议所需的数据趋势图。
下载:https://files.cnblogs.com/files/videring/GCLogViewer-0.3-win64.zip
浙公网安备 33010602011771号