JVM的参数类型:其中标配参数和x参数: 用的较多的是xx参数: 运行的java进程编号显示:jps -l ; 1)boolean类型: jinfo -flag 配置项 进程编号(或jinfo -flags 进程编号):用来查看某个进程的配置(在VM options中可以加参数):+(-)PrintGCDetails表示是否打印垃圾收集细节;+(-)UseSerialGC表示是否使用串行收集器; 2)KV(key,value)设置类型:-XX:属性key=属性值value; MetaspaceSize = 128m;MaxTenuringThreshold = 15;(由默认值改为所设置的值); 3)两个经典参数:-Xms : 等价于 -XX:InitialHeapSize ; -Xmx:等价于 -XX:MaxHeapSize; public class parameterTest {
public static void main(String[] args) throws InterruptedException {
System.out.println("--------");
Thread.sleep(Integer.MAX_VALUE);
}
} 查看JVM默认值:java -XX:+PrintFlagsInitial:主要查看初始默认;java -XX:+PrintFlagsFinal:主要查看修改更新,其中带 := 表示(JVM或人为)修改过的参数; java -XX:+PrintCommandLineFlags : 可以看到默认的垃圾回收器 使用程序打印jvm内存及结果: 常用参数:-Xss: 设置单个线程栈的大小,一般默认为512K~1024K;等价于:-XX:ThreadStackSize; 输出结果中0代表默认初始值-Xmn:设置年轻代大小;-XX:MetaspaceSize:设置元空间大小 ;常见参数配置: -Xms128m -XXmx4096m -Xss1024k -XX: MetaspaceSize=1024m -XX:+PrintFlagsFinal -XX:+PrintGCDetails -XX:+UseSerialGC-XX:+PrintGCDetails: 使用-XX:+PrintGCDetails (未设置其他参数)结果: 设置值使其OOM: GC: FullGC: -XX:SurvivorRatio:设置新生代中Eden和s0/s1空间的比例;默认:-XX:SurvivorRatio=8 eden:S0:S1 = 8:1:1 -XX:NewRatio:配置年轻代与老年代在堆结构的占比,默认-XX:NewRatio=2,年轻代占1,老年代占2,年轻代占整个堆的1/3 -XX:MaxTenuringThreshold:设置垃圾最大年龄;默认值为-XX:MaxTenuringThreshold=15;(可设置0~15) 引用的总体架构: 强引用:当内存不足,JVM开始垃圾回收,对于强引用对象,就算是出现了OOM也不会对该对象进行回收。在Java中最常见的就是强引用,把一个对象赋给一个引用变量,这个引用变量就是一个强引用。当一个对象被强引用变量引用时,它处于可达状态,是不可能被垃圾回收机制回收的。因此强引用时内存泄漏的主要原因。软引用:用java.lang.ref.SoftReference类来实现,可以让对象豁免一些垃圾收集。对于只有软引用的对象来说,当系统内存充足时它不会被回收,当系统内存不足时它会被回收;软引用通常用在对内存敏感的程序中,比如高速缓存就用到软引用,内存够用的时候就保留,不够用就回收。弱引用:用java.lang.ref.WeakReference类来实现;对于只有弱引用的对象来说,只要垃圾回收机制一运行,不管jvm的内存空间是否足够,都会回收该对象占用的内存。虚引用:用java.lang.ref.PhantomReference类来实现;它形同虚设,如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收,它不能单独使用也不能通过它来返回对象,虚引用必须和引用队列(ReferenceQueue)一起使用。PhantomReference的get()方法总是返回null; 虚引用的主要作用是跟踪对象被垃圾回收的状态,目的在这个对象被收集器回收的时候收到一个系统通知或者后续添加进一步的处理。Java技术允许使用finalize方法在垃圾收集器将对象从内存中清除之前做必要的清理工作。引用队列:ReferenceQueue是用来配合工作的;创建引用的时候可以指定关联的队列,当GC释放内存对象的时候,会将引用加入到引用队列。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动,这相当于是一种通知机制。实例: @Test
public void RefernceSTest(){
//强引用
Object object = new Object();
Object object1 = object;
object = null;
System.gc();
System.out.println(object1);//java.lang.Object@4ee285c6
}
@Test
public void SoftMemoryEnoughTest(){
//软引用
Object object = new Object();
SoftReference<Object> softReference = new SoftReference<>(object);
System.out.println(object);//java.lang.Object@4ee285c6
System.out.println(softReference.get());//java.lang.Object@4ee285c6
object = null;
System.gc();
System.out.println(object);//null
System.out.println(softReference.get());//java.lang.Object@4ee285c6
}
@Test
public void SoftMemoryNotEnoughTest(){
//JVM配置,故意产生大对象并配置小内存;
//-Xms5m -Xmm5m -XX:PrintGCDetails
Object object = new Object();
SoftReference<Object> softReference = new SoftReference<>(object);
System.out.println(object);//java.lang.Object@4ee285c6
System.out.println(softReference.get());//java.lang.Object@4ee285c6
object = null;
try{
byte[] bytes = new byte[30*1024*1024];
}catch (Throwable e){
e.printStackTrace();
}finally {
System.out.println(object);//null
System.out.println(softReference.get());//null
}
}
@Test
public void WeakTest(){
//弱引用
Object object = new Object();
WeakReference<Object> weakReference = new WeakReference<>(object);
System.out.println(object);//java.lang.Object@4ee285c6
System.out.println(weakReference.get());//java.lang.Object@4ee285c6
object = null;
System.gc();
System.out.println(object);//null
System.out.println(weakReference.get());//null
}
@Test
public void HashMapTest(){
HashMap<Integer,String> map = new HashMap<>();
Integer key = new Integer(1);
String value = "Value";
map.put(2,"value");
map.put(key, value);
System.out.println(map);//{2=value, 1=Value}
key = null;
System.out.println(map);//{2=value, 1=Value}
System.gc();
System.out.println(map);//{2=value,1=Value}
}
@Test
public void WeakHashMapTest(){
WeakHashMap<Integer,String> map = new WeakHashMap<>();
Integer key = new Integer(1);
String value = "Value";
map.put(key, value);
map.put(2,"value");
System.out.println(map);//{2=value, 1=Value}
System.out.println("----------");
key = null;
System.out.println(map);//{2=value, 1=Value}
System.out.println("----------");
System.gc();
System.out.println(map);//{2=value}
}
@Test
public void ReferenceTest() throws InterruptedException {
Object object = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
WeakReference<Object> weakReference = new WeakReference<>(object,referenceQueue);
System.out.println(object);//java.lang.Object@4ee285c6
System.out.println(weakReference.get());//java.lang.Object@4ee285c6
System.out.println(referenceQueue.poll());//null
System.out.println("------------");
object = null;
System.gc();
Thread.sleep(500);
System.out.println(object);//null
System.out.println(weakReference.get());//null
System.out.println(referenceQueue.poll());//java.lang.ref.WeakReference@621be5d1
}
@Test
public void PhantomTest() throws InterruptedException {
//虚引用
Object object = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
PhantomReference<Object> phantomReference = new PhantomReference<>(object,referenceQueue);
System.out.println(object);//java.lang.Object@4ee285c6
System.out.println(phantomReference.get());//null
System.out.println(referenceQueue.poll());//null
System.out.println("------------");
object = null;
System.gc();
Thread.sleep(500);
System.out.println(object);//null
System.out.println(phantomReference.get());//null
System.out.println(referenceQueue.poll());//java.lang.ref.WeakReference@621be5d1
}
假如有一个应用需要读取大量的本地图片:如果每次读取图片都是从硬盘中读取会严重影响性能;如果一次性全部加载到内存中又可能造成内存溢出; 使用软应用可以解决这个问题:用一个HashMap来保存图片的路径和相应图片对象关联的软引用之间的映射关系,在内存不足时,JVM会自动回收这些缓存图片对象所占用的空间,有效避免OOM问题: Map<String,SoftReference<BItmap>> imageCache = new HashMap< String,SoftReference<BItmap>>(); 常见OOM异常:属于 ErrorStackOverFlowErrorOutofMemeoryError: java heap spaceOutofMemeoryError: GC overhead limit exceeded : GC回收时间过长会抛出OutOfMemoryError:超过98%的时间用来做GC并且回收了不到2%的堆内存,连续多次GC都只回收了不到2%的极端情况下才会抛出。假如不抛出GC overhead limit 错误:GC清理出的内存很快又会被填满,迫使GC再次执行,形成恶性循环。CPU使用率一直是100%,而GC却没有任何成果。OutofMemeoryError: Direct buffer memory
写NIO程序经常使用ByteBuffer来读取或者写入数据,这是一种基于通道(Channel)与缓冲区(Buffer)的I/O方式。它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中提高性能,因为避免了在JVM堆和Native堆中来回复制数据。 byteBuffer.allocate(capability)是分配JVM堆内存,属于GC管辖范围,由于需要拷贝所以速度相对较慢。byteBuffer.allocateDirect(capability)是分配OS本地内存,不属于GC管辖范围,速度相对较快。 如果不断分配本地内存,堆内存很少使用,那么JVM就不需要执行GC,DirectByteBuffer对象们就不会被回收,这时候堆内存充足,但本地内存可能已经用光了,再次尝试分配本地内存时就会出现OutOfMemoryError
OutofMemeoryError: unable to create new native thread
高并发请求服务器时,经常出现此异常;准确地讲native thread异常与对应的平台有关。非root用户登入linux系统测试和服务器级别调参调优情况存在; 导致原因:1你的应用创建了太多线程,一个应用进程创建多个线程,超过系统承载极限。2服务器不允许应用程序创建这么多线程,linux系统默认允许单个进程创建1024个线程(非root用户)。 解决办法:1降低创建的线程数量。 2可以通过修改linux服务器配置,扩大linux默认限制。
OutofMemeoryError: Metaspace
Metaspace是方法区中在Hotspot中的实现,它与持久代最大的区别在于:Metaspace并不在虚拟机内存中而不是本地内存中 Class metadata(the virtual machines internal presentation of java class)存储于Metaspce的native memory 其中存放了一下信息:虚拟机加载的类信息(rt.jar包中java.lang包下中的类的信息)、常量池、静态变量、即时编译后的代码。 模拟Metadata空间溢出,我们不断生成类往元空间灌,类占据的空间总是会超过Metaspace指定的空间大小的
OOM示例代码: @Test
public void StackOverFlowErrorTest(){
stackover();//java.lang.StackOverflowError
}
private void stackover(){
//不停的调用自己,栈的大小默认512-1024K,使其出现StackOverFlowError
//递归调用,将栈空间称爆
stackover();
}
@Test
public void JVMHeapSpaceTest(){
//堆区内存不足异常
String str = "liu";
while (true){
str += str + new Random().nextInt(111111)+ new Random().nextInt(2222222);
str.intern();//不停的new对象,占空间
}//java.lang.OutOfMemoryError: Java heap space
}
@Test
public void GCOverHeadDemo(){
//-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
int i=0;
List<String> list = new ArrayList<>();
try{
while (true){
list.add(String.valueOf(++i).intern());
}
}catch (Exception e){
System.out.println("**************i:"+i);
e.printStackTrace();
throw e;
}//java.lang.OutOfMemoryError: GC overhead limit exceeded
}
@Test
public void DirectBufferMemoryTest(){
//-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
System.out.println("配置的maxDirectMemory:" +
(sun.misc.VM.maxDirectMemory()/(double)1024/1024) + "MB");
try{
Thread.sleep(3000);
}catch (InterruptedException e){
e.printStackTrace();
}
ByteBuffer bb = ByteBuffer.allocateDirect(6*1024*1024);
}//java.lang.OutOfMemoryError: Direct buffer memory
@Test
public void UnableNewNativeThreadTest(){
//需要配置linux为非root用户模式
for(int i = 0; ;i++){
System.out.println("---------------i = " + i);
new Thread(()->{
try {
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
},""+i).start();
}
}//OutofMemeoryError: unable to create new native thread
主要垃圾收集器:Serial,parallel,CMS,G1,ZGC 串行垃圾收集器:Serial:为单线程环境设计且只使用一个线程进行垃圾回收,会暂停所有的用户线程。所以不适合服务器环境。 并行垃圾收集器:parallel:多个垃圾收集线程并行工作,此时用户线程是暂停的,适用于科学计算、大数据处理首台处理等弱交互场景。 并发垃圾收集器:CMS(UseConcMarkSweepGC并发标记清除):用户线程和垃圾收集线程同时执行(不一定是并行,可能交替执行),不需要停顿用户线程;互联网公司多用它,适用对响应时间有要求的场景。 G1垃圾收集器将堆内存分割成不同的区域然后并发的对其进行垃圾回收。 默认的6种垃圾收集器: //-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseSerialGC (DefNew + Tenured)
//-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParNewGC (ParNew + Tenured)
//-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParallelGC (PSYoungGen + ParOldGen)
//-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParallelOldGC (PSYoungGen + ParOldGen)
//-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags (PSYoungGen + ParOldGen) (不加默认为这个)
//-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseConcMarkSweepGC (Par new generation + concurrent)
//-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseG1GC (PSYoungGen + ParOldGen)
CMS优点:并发收集低停顿 缺点:1并发执行,对CPU资源压力大 2采用的标记清除算法会导致大量碎片
G1比起CMS有两个优势: 1、G1不会产生内存碎片 2、可以精确控制停顿。该收集器是把整个堆划分成多个固定大小的区域,每次根据允许提供的时间去收集垃圾最多的区域。