一.请你谈谈实际的项目中在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问题和四大引用问题简述的更多相关文章

  1. POI导出Execl文件,使JAVA虚拟机OOM

    由于在项目中使用POI导出execl,导致JAVA虚拟机OOM,采用以下方式解决问题: 原先方式: g_wb = new XSSFWorkbook(sourceFile.getInputStream( ...

  2. 深入Java虚拟机

    第一章:Java体系结构介绍 1.Java为什么重要?       Java是为网络而设计的,而Java这种适合网络环境的能力又是由其体系结构决定的,可以保证安全健壮和平台无关的程序通过网络传播. 2 ...

  3. [转]JAVA虚拟机的生命周期

    JAVA虚拟机体系结构 JAVA虚拟机的生命周期 一个运行时的Java虚拟机实例的天职是:负责运行一个java程序.当启动一个Java程序时,一个虚拟机实例也就诞生了.当该程序关闭退出,这个虚拟机实例 ...

  4. 深入Java虚拟机读书笔记第五章Java虚拟机

    Java虚拟机 Java虚拟机之所以被称之为是虚拟的,就是因为它仅仅是由一个规范来定义的抽象计算机.因此,要运行某个Java程序,首先需要一个符合该规范的具体实现. Java虚拟机的生命周期 一个运行 ...

  5. Java虚拟机体系结构

    转自:http://www.cnblogs.com/java-my-life/archive/2012/08/01/2615221.html JAVA虚拟机的生命周期 一个运行时的Java虚拟机实例的 ...

  6. 深入理解java虚拟机之垃圾收集器

    Java一个重要的优势就是通过垃圾管理器GC (Garbage Collection)自动管理和回收内存,程序员无需通过调用方法来释放内存.也因此很好多的程序员可能会认为Java程序不会出现内存泄漏的 ...

  7. 深入理解java虚拟机---虚拟机工具jconsole(十八)

    Jconsole,Java Monitoring and Management Console. Jconsole是JDK自带的监控工具,在JDK/bin目录下可以找到.它用于连接正在运行的本地或者远 ...

  8. JVM总结-Java 虚拟机是怎么识别目标方法(上)

    重载与重写 在 Java 程序里,如果同一个类中出现多个名字相同,并且参数类型相同的方法,那么它无法通过编译.也就是说,在正常情况下,如果我们想要在同一个类中定义名字相同的方法,那么它们的参数类型必须 ...

  9. JAVA虚拟机体系结构JAVA虚拟机的生命周期

    一个运行时的Java虚拟机实例的天职是:负责运行一个java程序.当启动一个Java程序时,一个虚拟机实例也就诞生了.当该程序关闭退出,这个虚拟机实例也就随之消亡.如果同一台计算机上同时运行三个Jav ...

随机推荐

  1. H3C 10BASE-T线缆和接口

  2. js实现开关灯游戏

    需求: 点击三个按钮,页面出现不同数量的“灯” 所有的灯有相同的点击效果.点击一个灯的时候,this和他的上下左右都会变成另一种背景色. 代码在这里~~~ 文章地址 https://www.cnblo ...

  3. servicemix-3.2.1 内置的服务引擎和绑定组件

    服务引擎: servicemix-bean servicemix-camel servicemix-cxf-se servicemix-drools servicemix-eip servicemix ...

  4. CF1B.Spreadsheets(电子表格) 题解 模拟

    作者:zifeiy 标签:模拟 题目出处:Spreadsheets 题目描述 在流行的电子表格系统中(例如,在Excel中),使用如下计算方式来对列号进行计算. 第1列对应A,第2列对应B,--,第2 ...

  5. pycharm下的多个python版本共存(一)

    经历过IDLE,anaconda,和pycharn的编程环境,并进行了一段时间的项目编程后,决定使用pycharm作为以后的工作环境. 一方面因为项目组其他人推荐,另一方面在使用过程中比较顺手.当然很 ...

  6. 两种方法,轻松上手ConfigMap!

    属性配置文件在任何应用程序中都非常重要.它们不仅可以让应用程序具备灵活性,还能够根据文件中配置的值产生不同的功能.实际上,在staging.开发.测试.UAT或生产环境中,我们都使用属性配置文件来驱动 ...

  7. Vue CLI 介绍安装

    https://cli.vuejs.org/zh/guide/ 介绍 警告 这份文档是对应 @vue/cli 3.x 版本的.老版本的 vue-cli 文档请移步这里. Vue CLI 是一个基于 V ...

  8. 记录我的 python 学习历程-Day11 两个被忽视的坑、补充知识点、函数名的应用、新版格式化输出、迭代器

    补充知识点 函数形参中默认参数的陷阱 针对不可变数据类型,它是没有陷阱的 def func(name, sex='男'): print(name) print(sex) func('Dylan') # ...

  9. 聊聊多线程哪一些事儿(task)之 二 延续操作

    hello,又见面啦,昨天我们简单的介绍了如何去创建和运行一个task.如何实现task的同步执行.如何阻塞等待task集合的执行完毕等待,昨天讲的是task的最基本的知识点,如果你没有看昨天的博客, ...

  10. java_字段初始化的规律、静态方法中访问类的实例成员、查询创建对象的个数

    字段初始化规律: 当执行如下代码时 class InitializeBlockClass{ public int field=100; { field=200; } public Initialize ...