java虚拟机内存管理如何学习(java虚拟机内存管理基础知识点)

  • 时间:
  • 5547人关注

这是一篇关于java相关的编程问答内容,被650位程序员关注,内容涉及到java虚拟机、内存管理、java虚拟机内存管理基础知识点等,由聂瑞芬 编辑补充,一起来看下大家的回答。

    java内存区域和内存溢出

    HotSpotVM是SunJDK和OpenJDK中所带的虚拟机,也是目前使用范围最广的java虚拟机。

    如果一段java方法被调用次数达到一定程度,就会被判定为热代码交给JIT编译器即时编译为本地代码,提供运行速度,这就是HotSpot虚拟机名称的由来。

    一、内存区域构成

    1、程序计数器

    线程私有的内存。

    在虚拟机的概念模型里,字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、异常处理、线程恢复都需要依赖这个计数器来完成。

    为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储。

    消耗内存很少,可以忽略。

    2、java虚拟机栈

    线程私有的内存,生命周期和线程相同。常有人把内存分为堆和栈,这个栈就是虚拟机栈,或者虚拟机栈中的局部变量表部分。

    局部变量表存放了各种基本数据类型、对象引用和returnAddress(字节码指令的地址)

    3、本地方法栈

    与虚拟机栈作用相似。区别是本地方法栈为虚拟机执行java方法服务,而本地方法栈则为虚拟机使用到的Native方法服务,即由非java语言实现的方法。

    4、java堆

    所有线程共享,虚拟机启动时创建。

    堆是java虚拟机所管理的内存中最大的一块。唯一目的是存放对象实例,几乎所有对象实例都在这里分配。

    如果堆中没有内存来完成实例分配,且不可扩展将会抛出OutOfMemoryError。

    通过-Xmx和-Xms控制堆内存最大值最小值

    5、方法区

    线程共享的内存区域,用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。很多人习惯把Hotspot虚拟机中的方法区成为永久代,因为

    GC很少出现在这个区域。方法区无法满足内存分配需求时会跑出OutOfMemoryError。

    使用动态代理或CGLib这类字节码技术时,需要消耗很大的方法区来保证动态生成的Class能加载入内存。

    通过MaxPermSize控制方法区容量

    6、运行时常量池

    运行时常量池是方法区的一部分

    二、内存溢出

    Hotspot虚拟机中,对象在内存中存储的布局可以分为3块区域:对象头(Header)、实例数据(InstanceData)和对齐填充(Padding)

    1、堆溢出

    java.lang.OutOfMemoryError:javaheapspace

    2、虚拟机栈和本地方法栈溢出

    java.lang.StackOverflowError

    1

    3、方法区和运行时常量池溢出

    java.lang.OutOfMemoryError:PermGenspace

    1

    垃圾收集算法和内存分配策略

    主要是为了解决两个问题:内存分配与内存回收

    一、垃圾收集算法

    1、标记-清除算法

    首先标记出需要回收的对象,标记完成后统一回收。标记清除效率较低,而且会产生大量不连续的内存碎片。

    2、复制算法

    现在的商业虚拟机都采用这种收集算法来回收新生代。将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor。

    当回收时将Eden和Survior中还存活的对象一次性的复制到另一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间。

    Hotspot虚拟机默认Eden和Survivor大小比例是8:1,也就是说每次新生代中可用的内存空间为整个新生代容量的90%,只有10%的内存会被浪费。

    如果Survivor空间不够用时,需要依赖老年代进行分配担保。

    内存的分配担保就好像我们去银行贷款,如果信誉良好,银行会默认我们会如期还款,只需要有一个担保人保证如果不能还,可以从他账户里扣钱,这样对银行就没有风险了。

    如果另外一块Survivor空间没有足够空间存放上一次新生代收集下来的存活对象,这些对象直接通过分配担保机制进入老年代。

    3、标记-整理算法

    复制算法在对象存活率较高时要进行较多的复制操作,效率会降低。老年代采用这种算法,与标记清除不同的时,不是直接堆可回收的对象进行清理,而是让所有存活的对象

    向一端移动,然后直接清理掉边界以外的内存。

    4、分代收集算法

    一般把java堆分为新生代和老年代,新生代中每次垃圾收集都发现有大批对象死去,只有少量存活,那就选用复制算法,老年代因为对象存活率高,没有额外空间对他进行

    内存担保,必须使用标记-清理或标记-整理算法

    二、内存分配与回收策略

    1、对象优先在Eden分配

    -XX:+PrintGCDetails告诉虚拟机在发生垃圾收集行为时打印内存回收日志,并且在进程退出时输出当前的内存各区域分配情况。

    上面的设置可以限制java堆大小为20M不可扩展,其中新生代老年代大小分别为10M

    2、大对象直接进入老年代

    经常出现大对象容易导致内存还有不少空间时就提前触发GC以获取足够多的连续空间来安置它们,写程序时要尽量避免,更要避免短命大对象。

    虚拟机提供一个-XX:PretenureThreshold参数,上面的配置大于3M的对象直接在老年代分配,这样做的目的是避免在Eden区和两个Survivor区之间发生大量的内存复制。

    根据内存担保,Survivor区无法容纳的对象会直接进入到老年代。

    3、长期存活的对象将进入老年代

    对象在Survivor区每熬过一次GC年龄就增加一岁,当增加到一定程度(默认是15岁),就会晋升到老年代中。老年代做担保时需要老年代有足够多的空间,通过触发FullGC来收集。

    虚拟机性能监控命令与可视化工具

    命令行

    jps虚拟机进程状况工具

    jstat用于监视虚拟机各种运行状态信息的命令行工具

    jstack用于生成虚拟机当前时刻的线程快照,目的是定位线程长时间卡顿的原因

    可视化工具

    JConsole

以上就是码农之家java培训机构的小编针对“Java基础学习:java虚拟机内存管理”的内容进行的回答,希望对大家有所帮助,如有疑问,请在线咨询,有专业老师随时为你服务。

码农之家
精选回答2:浅谈java内存管理与内存溢出异常

9小时26分钟前回答

说到内存管理,笔者这里想先比较一下java与C、C++之间的区别:

在C、C++中,内存管理是由程序员负责的,也就是说程序员既要完成繁重的代码编写工作又要时常考虑到系统内存的维护

在java中,程序员无需考虑内存的控制和维护,而是交由JVM自动管理,这样就不容易出现内存泄漏和溢出的问题。然而,一旦出现内存泄漏和溢出方面的问题,如果不了解JVM的内存管理机制就很难找到错误所在。

1.JVM运行时数据区

JVM在运行java程序的时候会将它所管理的内存划分为若干个不同的区域,这些区域不仅有自己的用途,还有创建和销毁的时间。一般来说包含以下几个运行时数据区:

浅谈java内存管理与内存溢出异常

其中的橙色区域是各个线程私有的,即每个线程都会有自己的一份,而绿色区域是各个线程共享的。

2.java对象的创建

浅谈java内存管理与内存溢出异常

类加载检查

当JVM扫描到new关键字时,首先会去检查这个指令的参数是否能够在常量池中定位到一个类的符号引用,并且检查这个类的符号引用代表的类是否已被加载、解析和初始化过。如果没有,就必须先执行相应的类加载过程。

内存分配

当类加载检查通过后,JVM需要为新生对象分配内存,即是把一块确定大小的内存从java堆中划分出来。常用的划分方法有两种:指针碰撞(要求堆内存绝对规整)、空闲列表(堆内存并不规整)。

内存初始化

JVM需要将分配到的内存空间都初始化为零值(不包括对象头),这就保证了对象的实例字段在java代码中可以不赋初始值就直接使用,也就是说程序能够访问到这些字段的数据类型所对应的零值。

对象初始化

执行<init>方法,将对象按照程序员的意愿进行初始化。

3.对象的访问定位

对象创建好了,我们还希望能够快速的访问到这些对象,这就需要JVM栈上的reference(引用)数据来找到堆中的具体对象,而目前使用最多的访问方式有“句柄方式”和“直接指针”两种。

使用句柄方式访问的话,就需要在堆中划分一部分内存来作为句柄池,reference变量中存储的就是对象的句柄地址,而句柄中包含了对象实例数据和类型数据各自的具体地址信息。

浅谈java内存管理与内存溢出异常

使用直接指针访问的话,reference变量中存储的直接就是对象地址,但是需要考虑如何放置类型数据的相关信息。

浅谈java内存管理与内存溢出异常

4.内存溢出异常

JVM运行时数据区除了PC寄存器之外,其他的内存区域都有可能发生内存溢出的异常情况。PC寄存器是唯一一个在JVM规范中没有规定任何OutOfMemoryError(OOM)情况的区域

堆溢出

java中的堆用于存储对象实例,如果不断地创建对象,并且保证GC Roots到对象之间有可达路径以避免GC的回收处理,那么在对象的数量达到最大堆的容量限制后就会发生堆溢出的异常情况。

栈溢出(包括JVM栈和本地方法栈)

1.如果线程请求的栈深度大于JVM所允许的最大深度,将抛出StackOverflowError异常;

2.如果JVM在扩展栈时无法申请到足够的内存空间,将抛出OutOfMemoryError异常。

此外,还有方法区溢出、常量池溢出、本机内存溢出等等。

以上这篇浅谈java内存管理与内存溢出异常就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持码农之家。

展开问题

参考资料

  • 深入Java虚拟机:JVM G1GC的算法与实现

    深入Java虚拟机:JVM G1GC的算法与实现

    编辑推荐 结合实用JVM,图解Java垃圾回收机制的关键技术! 90张图表+33段代码,轻松理解G1GC算法原理 HotSpotVM源码剖析,深入探讨G1GC具体实现 1.深入Java虚拟机底层原理,详细解读经典GC算法; 2.理论结合实际,基于HotSpotVM源码探讨具体实现; 3.图文并茂、深入浅出,辅以大量插图和代码细致讲解。 内容简介 本书深入Java虚拟机底层原理,对JVM内存管理中的垃圾回收算法G1GC进行了详细解读。全书分为算法篇和实现篇两大部分:前一部分主要介绍G1GC的算法

    大小:129 MBJava虚拟机

    立即下载
  • Scala与Clojure函数式编程模式:Java虚拟机高效编程

    Scala与Clojure函数式编程模式:Java虚拟机高效编程

    本书向读者展示了如何采用函数式方案来替代或简化面向对象编程中使用的诸多通用模式,同时还介绍了一些在函数式世界中广泛使用的模式

    大小:34 MBJava虚拟机

    立即下载
  • Java虚拟机规范:Java SE 8版

    Java虚拟机规范:Java SE 8版

    书是Oracle官方发布,Java虚拟机技术创建人撰写,国内资深Java技术专家翻译,是深度了解Java虚拟机和Java语言实现细节的必读之作,它完整地讲述了由JavaSE8所引入的新特性 Java虚拟机规范(Jav

    大小:161.2 MBJava

    立即下载
  • 实战Java虚拟机:JVM故障诊断与性能优化

    实战Java虚拟机:JVM故障诊断与性能优化

    实战Java虚拟机:JVM故障诊断与性能优化 [葛一鸣] 2017年版

    大小:35.76MBJava虚拟机

    立即下载
  • 自己动手写Java虚拟机

    自己动手写Java虚拟机

    Java虚拟机非常复杂,要想真正理解它的工作原理,最好的方式就是自己动手编写一个! 本书是继《深入理解Java虚拟机》之后的又一经典著作,它一方面遵循《Java虚拟机规范》,一方面又独辟

    大小:4.27 MBJava

    立即下载
  • 揭秘Java虚拟机:JVM设计原理与实现

    揭秘Java虚拟机:JVM设计原理与实现

    《揭秘Java虚拟机》Java工程师高质量成长的必读本,谷歌官方认可的android开发语言Kotlin的基石。看透JVM设计思想与原理,彻底领悟JAVA编程精髓,以不变应万变!

    大小:186.2 MBJava虚拟机

    立即下载
  • 深入解析Java虚拟机HotSpot

    深入解析Java虚拟机HotSpot

    编辑推荐 (1)作者是阿里云Java技术专家,热衷于研究编程语言的设计与实现,对Java虚拟机和编译器都有较深入的研究。 (2)深入分析HotSpot VM的核心源码,全面解析运行时、垃圾回收器、即时编译器的实现原理和工作机制 (3)详细描述虚拟机底层实现,同时与上层Java应用和库相结合,为JVM的开发、调优和排错指明方向 内容简介 很多JVM的底层技术细节你是否只了解表面? 面对JVM Crash或性能调优方面的问题时你是否会束手无策? 面对上层Java应用发生

    大小:149 MBJava虚拟机

    立即下载

更多回答

33小时8分钟前回答

编程技术分享,Java虚拟机内存管理

内存模型 一说到内存管理,首先需要了解它的内存模型。 虚拟机的内存模型在jdk1.8之后有了一些变化,我们分开来看,请看下图: 由图我们可以看出,jdk每个版本都会有新生代和老年代,唯一不同的是小于1.8的版本为永久代,而大于等于1.8的版本去掉了永久代,转为元空间(Meta Space)。 永久代也就是存储的数据区里面的方法区,如果程序在运行中发生PermSpace溢出,则说明永久代内存不够,需要调整JVM参数增加永久代内存空间。 jdk1.8以后出现了MetaSpace,它和永久代不同的是,它的内存空间是动态扩展的,当然我们也可以设置MaxMetadaSpace来设置最大元空间内存数量,也就是在1.8以后设置PermSize是无效的。……

29小时15分钟前回答

java虚拟机深入学习之内存管理机制

前言 前面说过了类的加载机制,里面讲到了类的初始化中时用到了一部分内存管理的知识,这里让我们来看下Java虚拟机是如何管理内存的。 先让我们来看张图 有些文章中对线程隔离区还称之为线程独占区,其实是一个意思了。下面让我们来详细介绍下这五部分; 运行时数据区 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域,这些区域都拥有自己的用途,并随着JVM进程的启动或者用户线程的启动和结束建立和销毁。 先让我们了解下进程和线程的区别: 进程是资源分配的最小单位,线程是程序执行的最小单位。 进程有自己的独立地址空间,每启动一个进程,系统就……