Java核心技术面试精讲
杨晓峰
前Oracle首席工程师
立即订阅
43250 人已学习
课程目录
已完结 43 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 以面试题为切入点,有效提升你的Java内功
免费
模块一 Java基础 (14讲)
第1讲 | 谈谈你对Java平台的理解?
第2讲 | Exception和Error有什么区别?
第3讲 | 谈谈final、finally、 finalize有什么不同?
第4讲 | 强引用、软引用、弱引用、幻象引用有什么区别?
第5讲 | String、StringBuffer、StringBuilder有什么区别?
第6讲 | 动态代理是基于什么原理?
第7讲 | int和Integer有什么区别?
第8讲 | 对比Vector、ArrayList、LinkedList有何区别?
第9讲 | 对比Hashtable、HashMap、TreeMap有什么不同?
第10讲 | 如何保证集合是线程安全的? ConcurrentHashMap如何实现高效地线程安全?
第11讲 | Java提供了哪些IO方式? NIO如何实现多路复用?
第12讲 | Java有几种文件拷贝方式?哪一种最高效?
第13讲 | 谈谈接口和抽象类有什么区别?
第14讲 | 谈谈你知道的设计模式?
模块二 Java进阶 (16讲)
第15讲 | synchronized和ReentrantLock有什么区别呢?
第16讲 | synchronized底层如何实现?什么是锁的升级、降级?
第17讲 | 一个线程两次调用start()方法会出现什么情况?
第18讲 | 什么情况下Java程序会产生死锁?如何定位、修复?
第19讲 | Java并发包提供了哪些并发工具类?
第20讲 | 并发包中的ConcurrentLinkedQueue和LinkedBlockingQueue有什么区别?
第21讲 | Java并发类库提供的线程池有哪几种? 分别有什么特点?
第22讲 | AtomicInteger底层实现原理是什么?如何在自己的产品代码中应用CAS操作?
第23讲 | 请介绍类加载过程,什么是双亲委派模型?
第24讲 | 有哪些方法可以在运行时动态生成一个Java类?
第25讲 | 谈谈JVM内存区域的划分,哪些区域可能发生OutOfMemoryError?
第26讲 | 如何监控和诊断JVM堆内和堆外内存使用?
第27讲 | Java常见的垃圾收集器有哪些?
第28讲 | 谈谈你的GC调优思路?
第29讲 | Java内存模型中的happen-before是什么?
第30讲 | Java程序运行在Docker等容器环境有哪些新问题?
模块三 Java安全基础 (2讲)
第31讲 | 你了解Java应用开发中的注入攻击吗?
第32讲 | 如何写出安全的Java代码?
模块四 Java性能基础 (3讲)
第33讲 | 后台服务出现明显“变慢”,谈谈你的诊断思路?
第34讲 | 有人说“Lambda能让Java程序慢30倍”,你怎么看?
第35讲 | JVM优化Java代码时都做了什么?
模块5 Java应用开发扩展 (4讲)
第36讲 | 谈谈MySQL支持的事务隔离级别,以及悲观锁和乐观锁的原理和应用场景?
第37讲 | 谈谈Spring Bean的生命周期和作用域?
第38讲 | 对比Java标准NIO类库,你知道Netty是如何实现更高性能的吗?
第39讲 | 谈谈常用的分布式ID的设计方案?Snowflake是否受冬令时切换影响?
周末福利 (2讲)
周末福利 | 谈谈我对Java学习和面试的看法
周末福利 | 一份Java工程师必读书单
结束语 (1讲)
结束语 | 技术没有终点
Java核心技术面试精讲
登录|注册

第25讲 | 谈谈JVM内存区域的划分,哪些区域可能发生OutOfMemoryError?

杨晓峰 2018-07-03
今天,我将从内存管理的角度,进一步探索 Java 虚拟机(JVM)。垃圾收集机制为我们打理了很多繁琐的工作,大大提高了开发的效率,但是,垃圾收集也不是万能的,懂得 JVM 内部的内存结构、工作机制,是设计高扩展性应用和诊断运行时问题的基础,也是 Java 工程师进阶的必备能力。
今天我要问你的问题是,谈谈 JVM 内存区域的划分,哪些区域可能发生 OutOfMemoryError?

典型回答

通常可以把 JVM 内存区域分为下面几个方面,其中,有的区域是以线程为单位,而有的区域则是整个 JVM 进程唯一的。
首先,程序计数器(PC,Program Counter Register)。在 JVM 规范中,每个线程都有它自己的程序计数器,并且任何时间一个线程都只有一个方法在执行,也就是所谓的当前方法。程序计数器会存储当前线程正在执行的 Java 方法的 JVM 指令地址;或者,如果是在执行本地方法,则是未指定值(undefined)。
第二,Java 虚拟机栈(Java Virtual Machine Stack),早期也叫 Java 栈。每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个的栈帧(Stack Frame),对应着一次次的 Java 方法调用。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Java核心技术面试精讲》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(42)

  • I am a psycho
    如果仅从jvm的角度来看,要看下新生代和老年代的垃圾回收机制是什么。如果新生代是serial,会默认使用copying算法,利用两块eden和survivor来进行处理。但是默认当遇到超大对象时,会直接将超大对象放置到老年代中,而不用走正常对象的存活次数记录。因为要放置的是一个byte数组,那么必然需要申请连续的空间,当空间不足时,会进行gc操作。这里又需要看老年代的gc机制是哪一种。如果是serial old,那么会采用mark compat,会进行整理,从而整理出连续空间,如果还不够,说明是老年代的空间不够,所谓的堆内存大于100m是新+老共同的结果。如果采用的是cms(concurrent mark sweep),那么只会标记清理,并不会压缩,所以内存会碎片化,同时可能出现浮游垃圾。如果是cms的话,即使老年代的空间大于100m,也会出现没有连续的空间供该对象使用。

    作者回复: 非常不错的总结

    2018-07-03
    146
  • 石头狮子
    1,新生代大小过小。无法分配足够的内存。同时也老年代过小,导致提升失败。这时系统认为没有足够的空间存放该100M数据。
    2,栈可以抽象的看成计算资源。堆看成存储资源。计算资源不共享,不会发生线程安全问题。堆资源共享,
    容易发生线程安全问题。
    3,JAVA 封装了不同系统的线程模型,结果是在 java 内部有实现了一个通用的 java线程库。所以就需要用户内存来保存线程信息。
    2018-07-03
    15
  • Len
    从不同的垃圾收集器角度来看:

    首先,数组的分配是需要连续的内存空间的(据说,有个别非主流JVM支持大数组用不连续的内存空间分配🤔)。所以:

    1)对于使用年轻代和老年代来管理内存的垃圾收集器,堆大于 100M,表示的是新生代和老年代加起来总和大于100M,而新生代和老年代各自并没有大于 100M 的连续内存空间。

    进一步,又由于大数组一般直接进入老年代(会跳过对对象的年龄的判断),所以,是否可以认为老年代中没有连续大于 100M 的空间呢。

    2)对于 G1 这种按 region 来管理内存的垃圾收集器,可能的情况是没有多个连续的 region,它们的内存总和大于 100M。

    当然,不管是哪种垃圾收集器以及收集算法,当内存空间不足时,都会触发 GC,只不过,可能 GC 之后,还是没有连续大于 100M 的内存空间,于是 OOM了。

    作者回复: 很好的视角,g1 region之类确实有影响,另外g1还是有年代的概念的

    2018-07-03
    12
  • 鸡肉饭饭
    我们拿JDK7来说,有可能的原因是JVM的剩余内存有100M,但是它是分在不同年龄代的内存区域。

    因此应当单独的去查看每一块eden,survivor,old的大小,(通过SurvivorRatio知道s和e的比例大小,通过MaxNewSize知道young和old的比例)看看这三块区域是否有超过100M的内存大小。如果没有,就是因为没有一个区域能够再存储一个100M的对象。

    如果有,就可以通过工具查看下,每一块e s o每一块区域剩下的内存空间,如果没有一块内存大小超过100M,便是因为这个原因导致数组分配失败。
    2018-07-03
    11
  • 爱吃芒果的董先森
    因为给数组分配的是连续地址,而显示的是总的地址,不管是不是连续的。

    作者回复: 也对,最好综合考虑堆内存结构、gc区别等,后续会讲解

    2018-07-03
    7
  • 任鹏斌
    老师既然元数据区也存在溢出,那么为什么要用元数据区替换永久代呢,有什么好处吗?

    作者回复: metaspace 默认是自增的,永久带做不到

    2018-08-06
    5
  • tyson
    堆内存100M 包含了新生代(eden+s0+1)和老年代,大对象一般分配在老年代,那么最有可能在分配过程中老年代的空间不足。

    作者回复: 不错,可能性很多,其实和gc的选择也有关,例如g1 region比较小

    2018-07-03
    5
  • 鹅米豆发
    可能一,新生代没有足够的连续空间,且不能直接在老年代分配。比如E+S0+S1>100MB,但E<100MB,S0<100MB。

    可能二,大对象直接进入老年代,但老年代也没有足够的连续空间。参数+XX:PretenureSizeThreshold。

    可能三,线程数量太多,导致物理内存不足。

    可能四,直接内存使用太多,导致物理内存不足。

    作者回复: 不错,下一章会有更多内存结构细节

    2018-07-03
    5
  • 夏洛克的救赎
    Tomcat运行中突然出现java.lang.OutOfMemoryError: PermGen space有什么工具可以排查原因吗?

    作者回复: 简单点处理,可以:
    先看看永久带给了多大,如果太小,可以适当增大,使用'-XX:MaxPermSize=NNNm';
    如果没开启classunloading,可以根据GC选项做配置,例如,如果使用的CMS,可以加上“-XX:+CMSClassUnloadingEnabled -XX:+CMSPermGenSweepingEnabled”

    通常就能解决问题了,如果还是有问题,那就要看看是不是出现了classloader leak,常见做法如,取Heap dump,然后用类似Eclipse MAT这样的工具,看看有没有不回收的自定义classloader实例之类

    2019-01-14
    4
  • markin
    老师,能否跟我们介绍一下您平时获取资料的渠道。比如apache的一些开源项目,官网上就有很丰富的文档。但是我们获取jvm相关文档的渠道少之又少,无非就是博客或者书籍,这些都比较繁杂,并且可能参杂着很多难以识别的错误观点。授人以鱼不如授人以渔,先谢谢老师了。

    作者回复: Oracle官网也提供了很多好的文档:
    虚拟机规范 https://docs.oracle.com/javase/specs/jvms/se8/html/index.html
    诊断指南 https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/index.html
    调优指南 https://docs.oracle.com/javase/10/gctuning/
    Openjdk网站,或者那些感兴趣的邮件列表 http://mail.openjdk.java.net/mailman/listinfo
    YouTube上查查javaone, JVM summit之类
    回头有必要整理个书单之类

    但这些东西太多了,自己把握一下

    2018-07-08
    4
  • 师琳博
    100m的byte数组,一个byte对应一个引用,这样需要100m个的引用,所以需要的栈空间也不会低于100m,而对象的引用是在栈中分配的,(栈和堆加起来估计不低于200m)况且还是数组,对应的那么多引用还需要分配连续的内存空间,堆空间够的话,个人认为可能是栈空间不足造成的
    2018-07-03
    2
  • 刘p辉
    1.首先堆内存是分代的,总的内存超过100M,不能保证新生代,老年代都有足够的内存。
    2.为对象,数组分配内存需要连续的内存空间,有可能堆的总内存远超过要分配内存大小,但是在即使进行过垃圾回收(标记整理)后还是不存在足够的连续内存空间就会OOM。
    2018-07-30
    1
  • Geek_135148
    请问compressed class space区域怎么理解?是metasapce的一部分吗?如果是的话,有些采集工具为何会把它占的大小单独显示出来呢?而不直接显示metaspace的大小呢
    2018-07-29
    1
  • 代码狂徒
    老师,您是说方法区就是有永久代?那也就是说方法区在jdk8中已经不存在了?元数据区跟方法区有什么区别呢?那您的图是jdk7的图,有8得图吗?求解

    作者回复: 不是,方法区只是个逻辑概念,永久带和元数据区是具体设计、实现的选择;
    以前放到永久带,而且永久带内部还有类似intern字符串之类内容;
    元数据区具体内容和永久带也有区别,文章介绍了;
    那个图只是个简化示例,8去掉永久带就是了,具体到比较复杂的gc比如g1,就不是这个结构,请看后面讲

    2018-07-10
    1
  • Steven⁰⁰⁸
    数组是连续分配的,gc表明有多余100m,但有可能满足不了连续100m的空间,故会报OOME
    2018-07-04
    1
  • boom
    小白请教一个不相关的问题~java 内存模型跟 jvm内存模型的区别与联系是啥呢~

    作者回复: 怎么叫都有,看上下文,jsr133 jmm是解决多线程环境一致性,或者可以看做memory ordering model

    2018-07-03
    1
  • 三口先生
    堆内存比例设置不合理

    作者回复: 也对,回答比较简洁,哈哈

    2018-07-03
    1
  • sunlight001
    堆上有空间的划分,新生代和老年代,有可能新生代的空间不够,看到的是老年代的空间,个人猜测😃
    2018-07-03
    1
  • 未完的歌
    有可能是内存碎片化问题,或者是大对象的内存分配策略问题。
    需要了解一下积极垃圾收集算法,例如Mark Sweep就会造成内存碎片化问题,另外内存分配策略也是一个关注点。

    作者回复: 是的

    2018-07-03
    1
  • William
    思考题:
       大数组是需要连续内存空间的, 可能还存在总量够用的内存,但是并不是连续的 所以不可用.
    2019-11-28
收起评论
42
返回
顶部