深入理解jvm虚拟机一

  • JVM
    • Jvm基本概念
    • 运行过程
    • 内存区域
      • 程序计数器
      • 虚拟机栈
      • 本地方法栈
      • 方法区/永久代
      • 1.8元数据区
  • 回收与收集
    • 两大回收
      • 引用计数法
      • 可达性分析
    • 四大引用
      • 强引用
      • 软引用
      • 弱引用
      • 虚引用
  • 回收算法
    • 标记-清除(Mark-Sweep)
    • 复制(Copying)
    • 标记-整理(Mark-Compact)
    • 分代算法
      • 新生代
      • 老年代

JVM

Java与Jvm的关系似鱼和水,而开发者与Jvm的关系似情侣相爱相杀。爱它不用像C、C++摆弄指针,把内存控制的权利交给它,恨它一旦出现内存泄漏和溢出方面的问题,如果不理解它的话,无从下手,更别谈优化了。

Jvm基本概念

JVM及Java虚拟机,是可运行Java代码的假象计算机,Jvm是运行在操作系统之上的,它与硬件没有直接交互。

运行过程

我们都知道Java源文件,通过编译器,能够生成相应的.class文件,也就是字节码文件,而字节码文件又能通过jvm的解释器,编译成机器上的机器码,大概流程如下:Java源文件—>编译器—>字节码文件—>jvm—>机器码,虽然每个平台的解释器不同,但是虚拟机是相同的,这也就是为什么java是跨平台的。

内存区域

Jvm把Java程序运行时的内存划分为不同的数据区域,如图所示:

jvm内存区域主要分为线程私有(程序计数器、虚拟机栈、本地方法栈)、线程共享(堆、方法区)、直接内存,线程私有的生命周期与线程相同,依赖用户线程的启动/结束而创建/销毁,线程共享随虚拟机开启/关闭而创建/销毁。直接内存并不是jvm运行时数据区的一部分,在 JDK 1.4 引入的 NIO 提供了基于 Channel 与 Buffer 的 IO 方式, 它可以使用 Native 函数库直接分配堆外内存,然后使用DirectByteBuffer 对象作为这块内存的引用进行操作,这样就避免了在 Java堆和 Native 堆中来回复制数据,因此在一些场景中可以显著提高性能。

程序计数器

一块较小的内存空间,是当前线程所执行到的字节码的行号指令器,每个线程都是独立运行的,如果正在执行的是Java方法,则记录的是正在执行的虚拟机字节码行号,如果是native方法,则为空,此区域也是唯一一个在虚拟机中没有规定任何 OOM 情况的区域。

虚拟机栈

生命周期与线程相同,是描述Java方法执行的内存模型,每个方法在执行的时候都会创建一个栈针,用于存放局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行结束对应栈针在虚拟机中的入栈和出栈。如果虚拟机栈请求栈深度大于虚拟机所允许的深度,将抛出StrakOverflowError异常;如果扩展时无法申请到足够的内存,将抛出OOM异常,可以通过xss参数来调节大小,默认1M。

本地方法栈

与虚拟机中作用相似,区别是虚拟机栈为Java方法服务的,而本地方法栈是为native服务的,它也会抛出同虚拟机栈一样的异常。

是用来存放被创建的对象、数组,也是垃圾回收器进行垃圾回收的主要内存区域,可以通过xms、xmx来设置初始化堆大小、最大堆大小,默认是最小1/64,最大1/4。由于现代的垃圾回收器都是采用分代回收,因此堆从GC的角度还可以细分为:新生代(Eden区、From Survivor区、To Survivor区)和老年代。

方法区/永久代

用来存放被Jvm加载的类信息、常量、静态变量、即时编译器编译后的代码数据,HotSpot VM把GC分代收集扩展至方法区, 即使用Java堆的永久代来实现方法区, 这样 HotSpot 的垃圾收集器就可以像管理 Java 堆一样管理这部分内存,而不必为方法区开发专门的内存管理器(永久带的内存回收的主要目标是针对常量池的回收和类型的卸载,因此收益一般很小),通过设置PermSize和MaxPermSize设置初始化和最大上限(1.7以前)

1.8元数据区

在Jdk1.8取消了永久代,不在使用虚拟机内的永久代而是改用本地内存——元数据区(Metaspace),通过MetaspaceSize和MaxMetaspaceSize来设置初始化和最大上限。至于为什么不使用,理由如下:
1. 永久代调优很难,会为 GC 带来不必要的复杂度,并且回收效率偏低。
2. 字符串存在永久代中,容易出现性能问题和内存溢出。
3. 类及方法的信息等比较难确定其大小,因此对于永久代的大小指定比较困难,太小容易出现永久代溢出,太大则容易导致老年代溢出。
4. Oracle 可能会将HotSpot 与 JRockit 合二为一。

回收与收集

两大回收

在堆里几乎存放了所有的实例,垃圾回收前,需要判断哪些对象“活着”,哪些对象“死去”。

引用计数法

在java中引用和对象是有关联的,如果操作对象就必须用引用来进行。因此通过引用计数法来判断一个对象是否可以被回收,被引用则计数器+1,引用失效则计数器-1,直到引用计数为0,这是基本的也是比较高效的,但是无法避免相互引用。

可达性分析

为了避免相互引用问题,Java使用的可达性分析的方法,通过“GC roots”对象作为起点搜索,如果没有可达路径,则该对象不可达,可以被回收。即使这样,也不是说必须回收,如果该对象覆盖了finalize()方法,重新引用对象可以进行自救,只会自救一次。因为这个方法对运行代价高,不稳定性,因此也不推荐使用。在Java中GC roots对象包括下面四种:
1. 虚拟机栈(栈针中变量表)引用的对象
2. 本地方法栈(JNI)引用的对象
3. 方法区中类静态属性引用的对象
4. 方法区中常量引用的对象

四大引用

无论是引用计数法还是可达性分析,都与Java中引用有关。

强引用

Java中常见的引用,Object obj=new Object,即使虚拟机抛出OOM(内存溢出)也不会释放这部分引用,这部分容易造成内存泄漏。

软引用

需要用SoftReference类实现,描述一些还有用但非必需的对象,当系统内存足够时,是不会回收这部分资源,只有在系统内存不够时,这部分则会被回收。

弱引用

需要用WeakReference类实现,它比软引用存活时间更短,它的生命周期只存活在下一次垃圾回收之前。

虚引用

需要 PhantomReference 类来实现,随时被回收,它不能单独使用,必须和引用队列联合使用,虚引用的主要作用是跟踪对象被垃圾回收的状态。

回收算法

标记-清除(Mark-Sweep)

最基本的垃圾回收算法,分为两个阶段:标记、清除。标记可以被回收的对象,清除所有被标记的对象。
缺点:空间碎片化严重,标记清除,效率都不高。

复制(Copying)

为了解决碎片化问题,按内存容量一分为二,每次只用其中的一块,当一块存满时候,把存活的对象复制到另外一块上,把已使用的内存块清除。
缺点:内存使用率低。

标记-整理(Mark-Compact)

结合以上两个算法,标记类似于Mark-Sweep,标记后不是清理对象,而是把存活的对象移向内存的一端,把端以外的对象清除。

分代算法

分代算法是目前大多数JVM使用的回收算法,根据对象存活的不同生命周期来划分内存区域,一般化为老年代和新生代,老年代特点每次垃圾回收只有少量对象被回收,新生代特点每次垃圾回收都会有大量对象被回收,这样就可以根据不同区域来选择不同的回收算法。

新生代

目前大多数JVM新生代都是采用复制算法,因为新生代需要回收大部分对象,并不是按照1:1来划分新生代,而是划分为一块较大的Eden空间和两个较小的Survivor空间(from ,to),每次都使用Eden和一部分Survivor区域,当进行回收时,把存活的对象复制到另一块Survivor空间,通过xmn调整新生代大小。

对象优先分配在新生代,大对象直接则进入老年代(设置-xx:PretenureSizeThreshold参数),长期存活的对象进入老年代(设置-xx:MaxTenuringThreshold,默认15次),为了更加适应程序的内存状态,并不是要求年龄必须到15才会进入老年代,如果在Survivor空间中所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象也可以直接进入老年代。

老年代

老年代存放生命周期比较长的,对象相对稳定,所以采用标记清除或者整理。
Minor GC:当新生代内存不足,回收新生代(Ende区和两个Survivor)内存。
Major GC:清理老年代,一般伴随着一次Minor,效率比Minor要慢。
Full GC:清理整个堆空间,一般调优也就是堆fullGC进行优化,因为它会停止操作,单一线程进行GC。

深入理解jvm虚拟机一相关推荐

  1. java visualvm远程监控_深入理解JVM虚拟机12:JVM性能管理神器VisualVM介绍与实战

    本文转自互联网,侵删 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutori ...

  2. java虚拟机内存监控_深入理解JVM虚拟机9:JVM监控工具与诊断实践

    本文转自: https://juejin.im/post/59e6c1f26fb9a0451c397a8c 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到 ...

  3. java jvm垃圾回收算法_深入理解JVM虚拟机2:JVM垃圾回收基本原理和算法

    本文转自互联网,侵删 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 喜欢的话麻烦点下Star哈 文章将同步到我的个人博客: www.how ...

  4. 深入理解JVM虚拟机之垃圾回收

    深入理解JVM虚拟机之垃圾回收 什么叫做垃圾? 没有引用指向得对象都称为垃圾,好比如我们放风筝,哪些断了线得风筝都称之为垃圾. JVM怎么查找这些垃圾 一般又两种算法,1.可达性分析.2.引用计数 引 ...

  5. 接口多个实现类加载哪个_深入理解JVM虚拟机7:JNDI,OSGI,Tomcat类加载器实现

    本文转自互联网,侵删 本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutori ...

  6. 深入理解JVM虚拟机_4 JVM内部结构分析-栈

    深入理解JVM虚拟机_4 JVM内部结构分析-栈 作者:田超凡 原创博文,仿冒必究,部分素材转载自每特教育蚂蚁课堂 栈 1. Java 虚拟机栈也是线程私有的,它的⽣命周期和线程相同,描述的是 Jav ...

  7. 深入理解JVM虚拟机 - 自我编译JDK

    深入理解JVM虚拟机 - 自我编译JDK <深入理解JVM虚拟机>看过了好几遍了,对于编译一个JDK源码有很强的冲动.这里主要实战使用阿里云进行编译实战 为什么使用阿里云? 个人电脑奋斗四 ...

  8. 深入理解JVM虚拟机(九):运行期优化与JIT编译器

    1. JIT编译器的引入 首先我们这篇文章中所说的编译器都是指JVM的组成部分之一-即时编译器(JIT),与生成Java字节码的javac编译器要区分开来.首先我们这篇文章中所说的编译器都是指JVM的 ...

  9. 深入理解JVM虚拟机(二):垃圾回收机制

    谈起GC,应该是让Java程序员最激动的一项技术,我相信每个Java程序员都有探究GC本质的冲动!JVM垃圾回收机制对于了解对象的创建和对象的回收极为重要,是每个Java程序员必须掌握的技能. 本博客 ...

  10. 深入理解JVM虚拟机-Ubuntu中安装openJDK

    最近利用闲暇时间看了看<深入理解java虚拟机>来提高自身得知识储备,再这里准备将读书学习到得知识和个人的心得记录下来.首先调整好自己的心态,不要浮躁,因为编译阶段会无限踩坑. 1.安装V ...

最新文章

  1. AC日记——积木大赛 洛谷 P1969
  2. 分布式消息队列知识图谱
  3. mac svn .a文件的上传方法
  4. 多部门数据分析需求,如何满足?
  5. ITK:计算两个3D点之间的距离
  6. UVa140 Bandwidth 【最优性剪枝】
  7. 使用RMAN对数据文件进行恢复
  8. Oracle CoherenceWebLogic反序列化远程代码执行漏洞安全风险通告
  9. Google发布Chrome 8
  10. 如何在官网上下载MySQL驱动--最新方法
  11. catia今天突然打不开了_catia打不开的解答
  12. Python Recap 重新审视Python - 1 Python中的类
  13. android 手机短信恢复,安卓手机短信删除了怎么恢复?简单恢复的方法
  14. html点击超链接启动邮件客户端创建电子邮件
  15. Windows8 照片查看器,图片发黄解决方法~
  16. 产品经理 demo html,18个UI demo设计实例,深挖让用户愉悦的小惊喜
  17. 非财务人员的财务培训教(一.二)------财务基础知识
  18. 难道爱一个人有错吗 郑源
  19. [转载]软件常见的各种版本英文缩写
  20. 跟wms通信_应用与WMS的关联

热门文章

  1. 笨人学php好学吗_经典学经:笨人学数学的方法
  2. 视频教程-职场办公Excel技巧精粹灵活妙用集锦-Office/WPS
  3. 写给非网工的CCNA教程(7)通过VLAN连接实现同网段通信
  4. Wysistat与Webtrends比较
  5. Unity 窗口界面的简单介绍
  6. loadrunner 11下载及破解
  7. 75道程序员面试逻辑思维题
  8. Unity3D加载资源的四种方式
  9. 小米平板2wifi驱动下载_Xiaomi小米随身WiFi驱动下载
  10. 如何查看本机IP地址?