Java虚拟机OOM问题和四大引用问题简述
一.请你谈谈实际的项目中在Java虚拟机会抛出哪些异常,每个异常都是怎么产生的?
1.java.lang.StackOverflowError 栈空间满了
public static void stackOverFlow(){
// 递归调用之后,把栈空间塞满了,当程序出现递归调用没有终止的时候,就会出现此类错误
// Exception in thread "main" java.lang.StackOverflowError
stackOverFlow();
}
运行结果:
2.java.lang.OutOfMemoryError: Java heap space 堆空间满了
public static void outOfMemoryHeap() {
// 通过不停的生成新的string对象,把堆空间塞满,堆中没有在存放新的对象,此时会出现该错误
// Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
String str = "itheima";
while (true){
str += str + new Random().nextInt(111111)+new Random().nextInt(22222222);
str.intern();
}
}
运行结果:
3.java.lang.OutOfMemoryError: GC overhead limit exceeded GC回收时间过长
public static void outOfMemoryGC() {
/*
* GC回收时间过长时候会抛出OutOfMemoryError,过长的定义是指,超过98%的时间用来做GC并且回收了不到
* 2%的堆内存,连续多次GC都回收不到2%的极端情况下才会抛出.假如不抛出GC overflow limit会发生什么
* 呢?那就是GC清理的内存很快就会被再次填满,迫使GC再次执行,导致CPU的使用率一直在100%,但是GC却没有
* 任何成果.
* 配置JVM参数 -Xms10m -Xmx10m -XX:MaxDirectMemorySize=5m -XX:+PrintGCDetails
*
* java.lang.OutOfMemoryError: GC overhead limit exceeded
* */
int i = 0;
List<String> list = new ArrayList<>();
try {
while (true){
list.add(String.valueOf(++i).intern());
}
}catch (Throwable e){
e.printStackTrace();
System.out.println("i = "+i);
throw e;
}
}
运作结果:
4.java.lang.OutOfMemoryError: Direct buffer memory 外部内存满了
public static void outOfMemoryDbm() {
/* 写NIO的程序进场使用ByteBuffer来读取或写入数据,这是一种基于通道和缓冲区的I/O方式,它可以使用
Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块的引用
进行操作.这样可以在一些场景中显著提高性能,因为避免了Java堆和Native堆中来回复制内存
ByteBuffer.allocate(capability) 第一种方式是分配JVM堆内存,属于GC的范围,由于需要拷贝,所有
速度相对较慢
ByteBuffer.allocateDirect(capability)第一种方式是分配OS本地内存,不属于GC管辖的范围,所以
由于不需要进行内存拷贝所以速度相对较快 但如果不断分配本地内存,堆内存很少使用,那么JVM就不需要执行GCDirectByteBuffer对象们就不会被回收
这时候堆内存充足,但是本地内存已经使用光,再次尝试使用本地内存就会抛出OOM错误,JVM崩溃
JVM配置 -Xms10m -Xmx10m -XX:MaxDirectMemorySize=5m -XX:+PrintGCDetails
*
java.lang.OutOfMemoryError: Direct buffer memory
nio程序经常出现
*/ System.out.println("JVM最大可用内存: "+ (sun.misc.VM.maxDirectMemory() / (double)1024 / 1024 )+"MB");
try {
TimeUnit.MICROSECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(6 * 1024 *1024);
}
运行结果:
5.java.lang.OutOfMemoryError: unable to create new native thread 无法创建新的本地的线程
public static void outOfMemoryUcnt(){
/*
* 高并发请求服务器时候,会出现如下的错误,准确的来说该异常与对应的平台有关
* 导致原因:
* 1.应用创建了太多线程,一个应用程序创建了多个线程,超过了系统的承载
* 2.服务器并不允许应用程序创建过多的线程,Linux默认允许单个进程可以创建的线程数为1024个
* 应用程序创建的线程超过1024,就会抛出java.lang.OutOfMemoryError: unable to create new native thread
* 解决办法:
* 1.想办法降低应用程序创建的线程数量,分析应用程序是否真的需要创建这么多线程,如果不是,修改代码降低线程数
* 2.对于有的应用,确实需要创建很多线程,远超过Linux系统默认的1024个上限,可以通过修改Linux服务器配置
* 扩大Linux的默认限制 root用户没有限制
* ulimit -u 查看当前的用户下可以允许创建的线程数
* vim /etc/security/limits.d/90-nproc.conf 文件
* */
for (int i = 1; ; i++) {
System.out.println("i = "+i);
new Thread(()->{
try {
Thread.sleep(Integer.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
},String.valueOf(i)).start();
}
}
运行结果:Windows没有测出来
6.java.lang.OutOfMemoryError: Metaspace 元空间溢出
static class OOMTest{}; 内部类 public static void outOfMemoryMetaSpace(){
/*
* MetaSpace是方法区在HotSpot中的实现,它和持久区最大的区别在于:MetaSpace并不在虚拟机内存而使用本地内存
* 在JDK1.8中,class Metadata 被存储在MetaSpace的native memory
* 永久代(元空间)存放了以下信息
* 1. 虚拟机加载的类信息
* 2. 常量池
* 3. 静态变量
* 4. 即时编译后的代码
* 配置JVM: -XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=9m
*
* */
int i = 0;
try {
while (true){
i++; Enhancer e = new Enhancer();
e.setSuperClass(OOMTest);
e.setUseCache(false);
e.setCallBack(new MethodInterceptor(){
@Override
public Object intercept(Object o,Method method,Object[] objects,MethodProxy methodProxy) throws Throwable{
return methodProxy.invokeSuper(o,args);
}
});
e.create();
}
}catch (Throwable e){
System.out.println("第" + i+"次元空间溢出");
e.printStackTrace();
} }
运行结果:Windows没有出现!!!
二.请你能谈一谈Java的有哪些引用?JVM分别对不同的引用时怎样进行回收的?有什么作用?
1.强引用
当内存不足时,JVM会进行垃圾回收,对于强引用对象,就算出现OOM错误也不会对该对象进行垃圾回收,强引用是最常见的普通对象的引用,
只要还有强引用指向一个对象,就说明这个对象还活着,垃圾回收机制就不会回收这个对象,在Java中,最常见的引用就是强引用,把一个对象赋值
给一个引用变量,这个引用变量也是强引用当一个变量是强引用时,它处于可达的状态,不被垃圾回收机制回收的.即使该对象以后永远也用不到,JVM
也不会对其进行回收,因此强引用也是引发Java内存泄漏的主要原因之一.
对于一个普通的对象,如果没有其它得我引用关系,只要超过了引用的作用域,或者显式的把强引用赋值为null,一般就可以被垃圾回收机制回收.
// 强引用
public static void show01(){ Object o1 = new Object(); // new 出来的对象一般默认是强引用
Object o2 = o1; // o2 引用赋值 o2也是强引用
o1 = null; // 置空
System.gc(); // 手动GC垃圾回收
System.out.println(o1);
System.out.println(o2);
}
运行结果: o2没有被垃圾回收机制清除
2.软引用
软引用是相对于强引用弱化了一些的引用,需要使用java.lang.ref.SoftReference类来实现,可以让对象豁免一些垃圾回收,
对于软引用的对象来说,当系统的内存充足时,不会被垃圾回收机制回收,当系统的内存不足时,会被垃圾回收机制回收.
软引用通常用在一些对内存敏感的系统中,比如高速缓存就有用到软引用
//软引用
public static void show02(boolean flag){
if(flag){ // 内存充足
Object o1 = new Object();
SoftReference<Object> softReference = new SoftReference<>(o1);
System.out.println(o1);
System.out.println(softReference.get());
o1 = null;
System.gc();
System.out.println(o1);
System.out.println(softReference.get());
}else{ // 内存不充足设置JVM -Xms5m -Xmx5m -XX:+PrintGCDetails Object o1 = new Object();
SoftReference<Object> softReference = new SoftReference<>(o1);
System.out.println(o1);
System.out.println(softReference.get());
o1 = null;
System.gc();
try {
// 造成OOM,故意制造大对象
byte[] bytes = new byte[30 * 1024 * 1024];
}catch (Throwable e){
e.printStackTrace();
}finally {
System.out.println(o1);
System.out.println(softReference.get()); }
}
}
运行结果:
1.内存充足,o1被置空,但o2没有被回收
2.内存不足 o1,o2都被置为空
3.弱引用
需要使用java.lang.ref.WeakReference类来实现,它比软引用的生命周期更短
对于只有软引用的对象来说,只要垃圾回收机制一运行,不管JVM的内存空间是否充足,都会回收该对象所占的内存
用处:假如有一个应用,需要读取大量的本地图片,如果每次都从硬盘中读取,严重影响速度,如果全部加载到内存,又可能引发OOM
使用软引用/弱引用可以解决此类问题
使用一个HashMap来保存图片的路径和相应图片对象关联的软引用之间的映射关系,在内存不足时,
JVM会自动回收这些缓存图片所占用的空间,从而有效避免OOM问题
Map<String,SoftReference<BitMap>> imageCache = new HashMap<String,SoftReference<BitMap>>();
public static void show03(){
Object o1 = new Object();
WeakReference<Object> weakReference = new WeakReference<>(o1);
System.out.println(o1);
System.out.println(weakReference.get());
o1 = null;
System.gc();
System.out.println(o1);
System.out.println(weakReference.get());
}
运行结果: 只要一运行垃圾回收,内存就会被释放
4.虚引用
虚引用主要是通过java.lang.ref.PhantomReference类来实现的,与其他的引用类型不同,虚引用并不会决定对象的生命周期
如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都有可能被垃圾回收机制回收,它不能单独使用也不能
通过它访问对象,虚引用必须和引用队列来联合使用虚引用的主要作用是跟踪对象被垃圾回收的状态,就是这个对象被收集器回收
的时候收到一个系统的通知或者后续添加做进一步的处理
public static void show04(){
Object o1 = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
PhantomReference<Object> phantomReference = new PhantomReference<>(o1,referenceQueue); System.out.println(o1);
System.out.println(referenceQueue.poll());
System.out.println(phantomReference.get()); System.out.println("---------------------"); o1 = null;
System.gc();
System.out.println(o1);
System.out.println(referenceQueue.poll());
System.out.println(phantomReference.get());
}
运行结果:
1.虚引用的get()方法永远只返回null
2.在进行垃圾回收之后,在引用队列中可以看到此对象,主要就是在对象销毁前做出一些通知,类似于Spring的后置AOP
Java虚拟机OOM问题和四大引用问题简述的更多相关文章
- POI导出Execl文件,使JAVA虚拟机OOM
由于在项目中使用POI导出execl,导致JAVA虚拟机OOM,采用以下方式解决问题: 原先方式: g_wb = new XSSFWorkbook(sourceFile.getInputStream( ...
- 深入Java虚拟机
第一章:Java体系结构介绍 1.Java为什么重要? Java是为网络而设计的,而Java这种适合网络环境的能力又是由其体系结构决定的,可以保证安全健壮和平台无关的程序通过网络传播. 2 ...
- [转]JAVA虚拟机的生命周期
JAVA虚拟机体系结构 JAVA虚拟机的生命周期 一个运行时的Java虚拟机实例的天职是:负责运行一个java程序.当启动一个Java程序时,一个虚拟机实例也就诞生了.当该程序关闭退出,这个虚拟机实例 ...
- 深入Java虚拟机读书笔记第五章Java虚拟机
Java虚拟机 Java虚拟机之所以被称之为是虚拟的,就是因为它仅仅是由一个规范来定义的抽象计算机.因此,要运行某个Java程序,首先需要一个符合该规范的具体实现. Java虚拟机的生命周期 一个运行 ...
- Java虚拟机体系结构
转自:http://www.cnblogs.com/java-my-life/archive/2012/08/01/2615221.html JAVA虚拟机的生命周期 一个运行时的Java虚拟机实例的 ...
- 深入理解java虚拟机之垃圾收集器
Java一个重要的优势就是通过垃圾管理器GC (Garbage Collection)自动管理和回收内存,程序员无需通过调用方法来释放内存.也因此很好多的程序员可能会认为Java程序不会出现内存泄漏的 ...
- 深入理解java虚拟机---虚拟机工具jconsole(十八)
Jconsole,Java Monitoring and Management Console. Jconsole是JDK自带的监控工具,在JDK/bin目录下可以找到.它用于连接正在运行的本地或者远 ...
- JVM总结-Java 虚拟机是怎么识别目标方法(上)
重载与重写 在 Java 程序里,如果同一个类中出现多个名字相同,并且参数类型相同的方法,那么它无法通过编译.也就是说,在正常情况下,如果我们想要在同一个类中定义名字相同的方法,那么它们的参数类型必须 ...
- JAVA虚拟机体系结构JAVA虚拟机的生命周期
一个运行时的Java虚拟机实例的天职是:负责运行一个java程序.当启动一个Java程序时,一个虚拟机实例也就诞生了.当该程序关闭退出,这个虚拟机实例也就随之消亡.如果同一台计算机上同时运行三个Jav ...
随机推荐
- C#面向对象--命名空间与类库
1.命名空间 在源代码文件开头使用using语句引用 命名空间,就可以直接使用其中的类而不再需要指明其所属的命名空间. .NET Framework使用命名空间来管理所有的类. 类的修饰符: pu ...
- 2018-9-28-WPF-自定义-TextBoxView-的-Margin-大小
title author date CreateTime categories WPF 自定义 TextBoxView 的 Margin 大小 lindexi 2018-09-28 17:16:17 ...
- 在SuperSocket中启用TLS/SSL传输层加密
关键字: TLS, SSL, 传输层加密, 传输层安全, 证书使用, X509Certificate SuperSocket 支持传输层加密(TLS/SSL) SuperSocket 有自动的对TLS ...
- DOMjudge配置
DOMjudge配置补充 系统环境为 Debbian GNU/Linux 9 (stretch) 64-bit 在Web server configuration中, ln -s etc/apache ...
- java什么是方法(Method)?
方法是一组为了实现特定功能的代码块的集合.方法在语法上的功能主要有以下两个: ①:结构化代码 将代码按照功能进行组织,使代码的结构比较清晰,容易阅读和修改,也就是程序的可维护性强. ②:减少代码重复 ...
- win10 uwp 使用 AppCenter 自动构建
微软在今年7月上线 appcenter.ms 这个网站,通过 App Center 可以自动对数千种设备进行适配测试.快速将应用发送给测试者或者直接发布到应用商店.做到开发的构建和快速测试,产品的遥测 ...
- ZR1158
ZR1158 http://www.zhengruioi.com/contest/446/problem/1158 给定限制的问题大多数都是容斥或者二分,或者二分之后容斥 首先,这个问题的第一步我们还 ...
- LuoguP1402 酒店之王
LuoguP1402 酒店之王 最大流题目.带有一定的思维技(tao)巧(lu) 依旧分析题目.如果只有房间或者菜一种限制.那么就是一道裸的最大流了 可是两种条件都应当满足, 这貌似也可以做. 因为每 ...
- Cisco DNA-C POC环境配置
Step1:在DNA-C上创建Site,本例创建Global->China->WangJiang->20 F如下图: Step2:配置fusion区域的AAA和NTP等信息,如下图: ...
- mac 访达修改所有文件夹默认排序方式
先说个误区,下图只能改变当前目录的排序方式 修改所有目录的排序方式需要在顶部的“显示” 中修改