发布于2021-05-29 20:50 阅读(729) 评论(0) 点赞(10) 收藏(4)
往期文章推荐:
java常见面试考点(二十五):CAS是什么
java常见面试考点(二十六):集合类不安全
java常见面试考点(二十七):java里的锁总结
java常见面试考点(二十八):内部类详解
java常见面试考点(二十九):进程和线程的区别
【版权申明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权);
本博客的内容来自于:java常见面试考点(三十):常见异常;
学习、合作与交流联系q384660495;
本博客的内容仅供学习与参考,并非营利;
要理解Java异常处理是如何工作的,你需要掌握以下三种类型的异常:
堆栈溢出最常见的就是递归调用或者是循环调用了
package com.study.oom;
public class StackOverflowErrorDemo {
public static void main(String[] args) {
StackOverflowError();
}
private static void StackOverflowError() {
// Exceprion in thread "main" java.lang.StackOverflowError
StackOverflowError();
}
}
package com.study.oom;
import java.util.Random;
//-Xms10m -Xmx10m
public class JavaHeapSpaceDemo {
public static void main(String[] args) {
String str = "weiwozongheng";
while (true) {
// Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
str += str + new Random().nextInt(11111111) + new Random().nextInt(222222);
str.intern();
}
}
}
创建大对象,大数组都会产生这个错误。
GC回收时间过长时会抛出OutOfMemoryError。
过长的定义是,超过99%的时间用来做GC并且回收了不到2%的堆内存,连续多次GC都只是回收了不到2%的极端情况下才会抛出。 假如不抛出GC overhead limit 错误会发生什么情况呢? 那就是GC清理的这么点儿内存很快会被再次填满,迫使GC再次执行,这样就形成恶性循环。
CPU使用率一直是100%,而GC却没有任何成果。
package com.study.oom;
import java.util.LinkedList;
import java.util.List;
/**
* JVM参数配置演示
* -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
*
* GC回收时间过长时会抛出OutOfMemoryError。
* 过长的定义是,超过99%的时间用来做GC并且回收了不到2%的堆内存,连续多次GC都只是回收了不到2%的极端情况下才会抛出。
* 假如不抛出GC overhead limit 错误会发生什么情况呢?
* 那就是GC清理的这么点儿内存很快会被再次填满,迫使GC再次执行,这样就形成恶性循环。
* CPU使用率一直是100%,而GC却没有任何成果。
*/
public class GCOverheadDemo {
public static void main(String[] args) {
int i = 0;
List<String> list = new LinkedList<String>();
try {
while (true) {
list.add(String.valueOf(i++).intern());
}
} catch (Throwable throwable) {
System.out.println("********** i = " + i);
throwable.printStackTrace();
throw throwable;
}
}
}
出现异常
java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.lang.Integer.toString(Integer.java:401)
at java.lang.String.valueOf(String.java:3087)
at com.study.oom.GCOverheadDemo.main(GCOverheadDemo.java:22)
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.lang.Integer.toString(Integer.java:401)
at java.lang.String.valueOf(String.java:3087)
at com.study.oom.GCOverheadDemo.main(GCOverheadDemo.java:22)
写NIO程序经常使用ByteBuffer来读取或者写入数据,这是一种基于通道(Channel)与缓冲区(Buffer)的I/O方式。
它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。
这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中唠会复制数据。
ByteBuffer.allocate(capability) 第一种方式:
ByteBuffer.allocateDirect(capability) 第二种方式:
但如果不断分配本地内存,堆内存很少使用,那么JVM就不需要执行GC,DirectByteBuffer对象们就不会被回收。这时候堆内存充足,但本地内存可能已经使用光了,再次尝试分配本地内存就会出现OutOfMemoryError,那程序就直接崩溃了。
package com.study.oom;
import java.nio.ByteBuffer;
/**
* 配置参数:
* -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
*
* 故障现象:
* Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
*
* 导致原因:
* 写NIO程序经常使用ByteBuffer来读取或者写入数据,这是一种基于通道(Channel)与缓冲区(Buffer)的I/O方式。
* 它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。
* 这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中唠会复制数据。
*
* ByteBuffer.allocate(capability) 第一种方式,分配JVM堆内存,属于GC管辖范围,由于需要拷贝所以速度相对较慢。
* ByteBuffer.allocateDirect(capability) 第二种方式, 分配OS本地内存,不属于GC管辖范围,由于不需要内存拷贝所以速度相对较快
*
* 但如果不断分配本地内存,堆内存很少使用,那么JVM就不需要执行GC,DirectByteBuffer对象们就不会被回收。
* 这时候堆内存充足,但本地内存可能已经使用光了,再次尝试分配本地内存就会出现OutOfMemoryError,那程序就直接崩溃了。
*/
public class DirectBufferMemoryDemo {
public static void main(String[] args) {
System.out.println("配置的maxDirectMemory: " + (sun.misc.VM.maxDirectMemory() / 1024 / 1024) + "MB");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(6 * 1024 * 1024);
}
}
Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
翻译意思:不能再创建多个新的本地线程了,你的程序不管是微服务部署还是Linux系统,你目前创建的线程已经达到了,不能再创建了(大厂的高并发故障)
高并发请求服务器时,经常出现如下异常:
java.lang.OutOfMemoryError: unable to create new native thread
准确地讲,该native thread异常与对应的操作系统平台有关。
导致原因:
(1) 应用创建了太多的线程,一个应用进程创建多个线程,超过系统承载极限。
(2)服务器并不允许你的应用程序创建这么多的线程,linux系统默认允许单个进程创建的线程数是1024个。
应用创建线程超过这个数量,就会报java.lang.OutOfMemoryError: unable to create new native thread
解决办法:
在Linux部署
注意:不要用root用户去操作
(1)在UnableCreateNewThreadDemo.java 文件拷贝到Linux环境中
[MrZhou@spark2 demo]$ ll
total 4
-rw-r--r--. 1 root root 469 Aug 1 17:33 UnableCreateNewThreadDemo.java
(2)由于带了包名,所以先编译
[MrZhou@spark2 demo]$ javac -d . UnableCreateNewThreadDemo.java
(3)再次去查看,发现多了"com"的包名
[MrZhou@spark2 demo]$ ll
total 4
drwxr-xr-x. 3 root root 19 Aug 1 17:36 com
-rw-r--r--. 1 root root 469 Aug 1 17:33 UnableCreateNewThreadDemo.java
(4)运行
//运行
[MrZhou@spark2 demo]$ java com.study.oom.UnableCreateNewThreadDemo
>>>>>>>>>> i = 1
>>>>>>>>>> i = 2
.................
.................
>>>>>>>>>> i = 4086
//不能再创建更多线程,并且这个ctrl+c不能强制退出的,需要杀进程才能退出
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method)
at java.lang.Thread.start(Thread.java:717)
at com.study.oom.UnableCreateNewThreadDemo.main(UnableCreateNewThreadDemo.java:15)
(5)查看进程&杀进程
//查看进程
[root@spark2 ~]# ps -ef|grep java
MrZhou 10171 10155 4 18:02 pts/0 00:00:04 java com.study.oom.UnableCreateNewThreadDemo
root 14304 14270 0 18:03 pts/1 00:00:00 grep --color=auto java
[root@spark2 ~]#
//杀进程
[root@spark2 ~]# kill -9 10171
[root@spark2 ~]#
//再查看启动的程序,发现已经退出了
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
at java.lang.Thread.start0(Native Method)
at java.lang.Thread.start(Thread.java:717)
at com.study.oom.UnableCreateNewThreadDemo.main(UnableCreateNewThreadDemo.java:15)
Killed
OOM之unable to create new native thread上限调整
服务器调优参数
//查看用户默认是参数是多少
[MrZhou@spark2 demo]$ ulimit -u
1024
//默认是1024
//修改默认值
[MrZhou@spark2 demo]$ vim /etc/security/limits.d/20-nproc.conf
Java8及之后的版本使用Metaspace来替换永久代。
Metaspace是方法区在HotSpot中的实现,它与永久代最大的区别在于:Metaspace并不在也即在java8中,class metadata(the virtual machines internal presentation od java
class)虚拟机内存中而是使用本地内存,被存储在叫做Metaspace的native memory。
永久代(java8后被元空间(Metaspace)取代了)存放了以下信息:
虚拟机加载的类信息、 常量池、 静态变量、 即时编译后的代码
package com.study.oom;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* JVM参数:-XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=8m
*
* Java8及之后的版本使用Metaspace来替换永久代。
*
* Metaspace是方法区在HotSpot中的实现,它与永久代最大的区别在于:Metaspace并不在虚拟机内存中而是使用本地内存。
* 也即在java8中,class metadata(the virtual machines internal presentation od java class)
* 被存储在叫做Metaspace的native memory。
*
* 永久代(java8后被元空间(Metaspace)取代了)存放了以下信息:
* 虚拟机加载的类信息
* 常量池
* 静态变量
* 即时编译后的代码
*
* 模拟Metaspace空间溢出,我们不断生成类往元空间灌,类占据的空间总是会超过Metaspace指定的空间大小的。
*/
public class MetaspaceDemo {
static class OomTest {}
public static void main(String[] args) {
int i = 0; //模拟计数多少次以后发生异常
try {
while (true) {
i++;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OomTest.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor(){
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
return methodProxy.invokeSuper(o,args);
}
});
enhancer.create();
}
}catch (Throwable e){
System.out.println("***************多少次后发生异常:"+i);
e.printStackTrace();
}
}
}
原文链接:https://blog.csdn.net/qq_44159782/article/details/117293095
作者:举起你的手来
链接:http://www.javaheidong.com/blog/article/207507/628188ddc1fd6d345ca6/
来源:java黑洞网
任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任
昵称:
评论内容:(最多支持255个字符)
---无人问津也好,技不如人也罢,你都要试着安静下来,去做自己该做的事,而不是让内心的烦躁、焦虑,坏掉你本来就不多的热情和定力
Copyright © 2018-2021 java黑洞网 All Rights Reserved 版权所有,并保留所有权利。京ICP备18063182号-2
投诉与举报,广告合作请联系vgs_info@163.com或QQ3083709327
免责声明:网站文章均由用户上传,仅供读者学习交流使用,禁止用做商业用途。若文章涉及色情,反动,侵权等违法信息,请向我们举报,一经核实我们会立即删除!