Table of Contents

虚拟机内存结构详解

程序计数器

虚拟机栈 JVM stack

本地方法栈 native method stack

常量池

堆 heap

方法区 method area

为什么要放弃永久代permnent generation?

局部变量和类变量内存布局

String对象存储?

对象的内存布局?虚拟机栈中的引用如何和堆中的对象产生关联的?

句柄方式:

直接指针方式:

垃圾收集

对象需要进行垃圾收集吗?

可以视为GC root的对象都有哪几种?​

垃圾收集算法分析

问题:eden survivor区的比例,为什么是这个比例,eden survivor的工作过程?

是否可以GC直接内存?

HotSpot的算法实现

安全点与安全区域

常用的JVM调优参数

dump文件分析

java有没有主动触发GC的方式?

java内存模型与线程, happens before原则,内存模型与前面的内存区域的区别?

主内存与工作内存

与java内存区域的区别

happens-before原则

jvm的优化

虚拟机类加载机制及类加载执行子系统


虚拟机内存结构详解

在jdk1.8之前的虚拟机管理的内存如图

在jdk1.8之中,发生了一些变化,虚拟机栈和本地方法栈合二为一了,但是可以想到的是他们在内部的逻辑必然仍然是按照虚拟机栈和本地方法栈划分。jdk1.8的虚拟机管理的内存如图:

我们主要关注的是运行时数据区.而运行时数据区当中白色的几块为线程私有的,灰色的堆以及方法区是线程共享的数据区,因此GC针对的区域也是这两个区域:即堆和方法区。其中方法区的垃圾收集行为是可配的,也比较少出现。而GC并不针对其他区域的原因是程序计数器,虚拟机栈,本地方法栈3个区域随线程而生,随线程而灭,因此不会存在需要垃圾收集的情况。

程序计数器

  1. 当前字节码执行的行号
  2. 线程私有
  3. 没有oom异常

虚拟机栈 JVM stack

  1. 线程私有
  2. 每个方法在执行时,会创建一个栈帧,即stack frame,用于存储方法的局部变量表,动态链接,方法出口等信息。这也从内存角度证明了局部变量的线程安全性。同时,还证明了在一个类编译出来的Class文件里面,是不会有这个类的方法的,只会包括这个类的成员变量,以及对象头信息等。
  3. 每个方法执行过程->对应一个栈帧的入栈和出栈过程
  4. 局部变量表,即存放方法的局部变量,有两种,一是基本数据类型,如int等,二是对象引用。

    (1)当声明是基本类型的变量的时,其变量名及值(变量名及值是两个概念)是放在方法栈中

    (2)当声明的是引用变量时,所声明的变量(该变量实际上是在方法中存储的是内存地址值)是放在方法的栈中,该变量所指向的对象是放在堆类存中的。

  5. 当线程请求栈深度大于虚拟机允许的深度时->stackoverflow
  6. 虚拟机栈无法申请到足够内存时->oom

本地方法栈 native method stack

  1. 与虚拟机栈几乎一样,只是虚拟机栈为java方法服务,而本地方法栈为native方法服务

常量池

一块特殊的内存区域,存放常量,如基本类型的包装类(Integer、Short)和String。在jdk1.7以及以前,常量池存放在方法区(永久代),注意常量池位于堆中。

在jvm规范中,每个类型都有自己的常量池。常量池是某类型所用常量的一个有序集合,包括直接常量(基本类型,String)和对其他类型、字段、方法的符号引用。之所以是符号引用而不是像c语言那样,编译时直接指定其他类型,是因为java是动态绑定的,只有在运行时根据某些规则才能确定具体依赖的类型实例,这正是java实现多态的基础。

在JVM中,类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载7个阶段。而解析阶段即是虚拟机将常量池内的符号引用替换为直接引用的过程。

堆 heap

  1. 是虚拟机管理的最大块内存,唯一目的存放对象实例。也叫GC堆。
  2. 所有线程共享
  3. 唯一目的存放对象实例,但现在已不再绝对,也叫GC堆
  4. 可分为新生代和老年代,默认比例为1:2。新生代细分可分为Eden空间,From Survivor空间,To Survivor空间。注意新生代和老年代只是针对堆的概念。问题:为什么老年代是新生代的2被?答:若老年代过小,会频繁触发full gc影响效率。
  5. 对新生代的GC称为Minor GC,因为java对象大多朝生夕灭,minor gc非常频繁,回收速度也非常快。对老年代的gc称为Full GC/Major GC,通常同时会伴随一次Minor GC,但不绝对。Major GC速度一般逼Minor GC慢10倍以上
  6. 在物理上是不连续的
  7. 无法再分配实例时oom

方法区 method area

  1. 线程共享
  2. 存储类信息,静态变量(即类变量,static变量的位置在这),常量,即时编译器编译之后的代码等。

    Person p = new Persoon("小明",18);
    p  是指针,存放在栈中。
    new Persoon("小明",18) 是对象 ,存放在堆中。
    Person 类的相关信息存放在方法区。
    引申 :对象的实例保存在堆上,对象的元数据(instantKlass)保存在方法区,对象的引用保存在栈上。

  3. 内存不够用时oom
  4. 注意,方法区是jvm规范定义的一个区域,准确的说这是一个概念,而在hotspot虚拟机当中,使用永久代PermGen-Permanent Generation,实现了方法区这一规范。因此方法区和永久代并不等价。而其他虚拟机则有各自对方法区的实现。不过,在jdk1.8之后方法区已经被移除,用metaspace替代了。因此我们可以说jdk1.8当中方法区的实现是metaspace也就是说方法区在jdk1.8之前的实现是永久代,1.8之后是metaspace元空间。
  5. 运行时常量池是方法区的一部分,用于存放在编译期生成的各种字面量和符号引用。

在java8中JVM的PermGen空间被移除:取代它的是Metaspace。metaspace的存放内存区域不再是虚拟机的内存而是本地内存。这大大的减少了这个区域出现OOM的情况。

JDK1.6 Java 类信息、常量池、静态变量都存储在 Perm(永久代)里。类的元数据和静态变量在类加载的时候分配到 Perm,当类被卸载的时候垃圾收集器从 Perm 处理掉类的元数据和静态变量。当然常量池的东西也会在 Perm 垃圾收集的时候进行处理。
JDk1.7 常量池移到了堆中,符号引用转移到了native heap,类的静态变量转移到了java heap
JDK1.8

JDK 1.8 的对 JVM 架构的改造将类元数据放到本地内存中(即metaspace),另外,将常量池和静态变量放到 Java 堆里。HotSopt VM 将会为类的元数据明确分配和释放本地内存。

这样类的元素信息就可以突破 -XX:MaxPermSize 的限制,可以使用更多的本地内存。解决了原来在运行时生成大量类的造成经常 Full GC 问题,如运行时使用反射、代理等。

从发版历史来说,从jdk1.7开始,jvm开始逐步淘汰PermGen,在jdk1.7当中转移了一部分存储的变量,在jdk1.8当中完全移除。

如下图所示:

我们可以在JVM启动的时候通过-XX:MaxMetaspaceSize 指定Metaspace的大小。如果不指定的话,则metaspace空间的大小会在本地内存当中自动增大。若指定了大小,则Metaspace的空间超过最大值之后,jvm仍然会抛出OOM错误。

对方法区和永久代的理解以及它们之间的关系?

方法区:一个概念上的定义。

永久代:方法去这个概念的一个具体实现。

什么要放弃永久代permnent generation?

在jdk1.8当中虚拟机的永久代permnent generation被替换为了metaspace,元数据区,其中的一些理由如下:

  • PimMGEN所需的大小很难预测。内存供应不足将会触发java.lang.OutOfMemoryError:Permgen大小过大则导致资源浪费。
  • GC性能改进,使并发类数据分配不需要GC中断和元数据上的特定迭代器。简单点说就是提升性能。
  • 支持进一步的优化,例如G1收集器的并行类卸载

对象的内存布局?虚拟机栈中的引用如何和堆中的对象产生关联的?

在Hotspot虚拟机当中,对象在内存中存储的布局分为三块区域:对象头header,实例数据Instance Data,对齐填充 Padding。如下图:

对象头

Mark Word: HashCode,GC分代年龄,锁状态标志,线程持有的锁,偏向线程ID,偏向时间戳等

类型指针:指向对象的类元素的指针。JVM通过这个指针确定这个对象是哪个类的实例。

若对象是数组,对象头还需要记录数组长度。

实例数据则是各种类型的字段:包括子类定义的,和从父类继承的

而对象的访问定位。两种方式主要有:

句柄方式:

引用中存储的是对象的句柄地址,而这些句柄存储在堆中的句柄池中,而池中的句柄存储了对象实例数据地址和对象类型数据的地址,通过对象实例数据地址可以在堆中找到对象实例数据,通过对象类型数据地址可以在方法区中找到类型数据. 例子如下:

优点:句柄方式最大的优点是当对象被移动的时候,只会改变句柄中对象实例数据地址,而引用(reference)本身中句柄地址不变。

直接指针方式:

引用中存储的是对象地址,通过对象地址在堆中可以找到对象实例数据,而对象实例数据中存储了类型数据的指针,通过这个指针可以到方法区中找到类型数据,即是哪个类的对象。例子如下:

优点: 减少了一次指针定位的时间开销,速度更快,HotSpot虚拟机正使用了这种方式。

关于局部变量和类变量内存布局

一:在方法中声明的变量,即该变量是局部变量,每当程序调用方法时,系统都会为该方法建立一个方法栈,其所在方法中声明的变量就放在方法栈中,当方法结束系统会释放方法栈,其对应在该方法中声明的变量随着栈的销毁而结束,这就局部变量只能在方法中有效的原因

在方法中声明的变量可以是基本类型的变量,也可以是引用类型的变量。

(1)当声明是基本类型的变量的时,其变量名及值(变量名及值是两个概念)是放在方法栈中

(2)当声明的是引用变量时,所声明的变量(该变量实际上是在方法中存储的是内存地址值)是放在方法的栈中,该变量所指向的对象是放在堆类存中的。

二:在类中声明的变量是成员变量,也叫全局变量,放在堆中的(因为全局变量不会随着某个方法执行结束而销毁)。

同样在类中声明的变量即可是基本类型的变量 也可是引用类型的变量

(1)当声明的是基本类型的变量其变量名及其值放在堆内存中的

(2)引用类型时,其声明的变量仍然会存储一个内存地址值,该内存地址值指向所引用的对象。引用变量名和对应的对象仍然存储在相应的堆中

关于String对象的内存布局

String是一个特殊的包装类数据,可以用:

String str = new String("abc");String str = "abc";

两种形式来创建,第一种是用new来创建对象的,它会在堆中按照new对象的流程创建一个新的对象出来,每调用一次就会创建一个新的对象;而第二种是先在栈中创建一个对String类的对象引用str ,然后查找常量池中有没有存放"abc",如果没有,则将"abc"存放常量池,并令str 指向"abc",如果已经有"abc",则直接令str指向"abc"。

关于这部分的详细内容可以参考:[Java] String类解析。

垃圾收集

关于Java垃圾收集机制,需要重点把握两个问题

  • 哪些对象需要收集
  • 如何进行收集

下面我们就围绕这两个问题进行展开

对象需要进行垃圾收集吗?

引用计数法:给对象中添加一个引用计数器,有一个地方引用它,计数器加一,引用失效就减1,计算器为0的对象就是不再被使用的。

存在的问题:两个或多个对象之间可能会互相引用对方,导致这样的对象无法被回收。

可达性算法:通过一系列的称为"GC Roots"的对象作为起点,从这些起点开始向下搜索,搜索所走过的路径称为Reference Chain引用链,当一个对象到GC Roots没有任何引用链相连时,证明此对象是不可用的。这种方式显然解决了引用计数法的问题。

关于gc root的用法,下面这个图很好的进行了说明:

可以视为GC root的对象都有哪几种?

  • 虚拟机栈(栈帧中的本地变量表)中引用对象
  • 方法区中的类静态属性引用的对象
  • 方法区中常量引用的对象(final 的常量值)
  • 本地方法栈JNI(即一般说的Native方法)引用的对象

除了本地方法栈的内存图如下:

关于本地方法栈,它和虚拟机栈存在着下面这样一种调用关系:

java虚拟机内部线程执行的情况,可能一直在执行java方法,操作java栈;也可能在java栈和本地方法栈中来回切换。

垃圾收集算法分析

GC主要是针对java堆的操作,那么GC算法自然也就只和java堆的对象有关。GC算法的前提是前面提到的GC roots可达性算法,通过可达性算法确定哪些对象能被回收是GC算法的前提。GC算法准确的说一共三种

1.标记清除(基础算法)

  • 阶段一 标记,在可达性算法的同时标记应该被回收的对象
  • 阶段二 清除,清除所有被标记为需回收的对象

但是有两个问题

  • 效率:标记再清除的效率不高
  • 空间问题:清除之后会产生大量不连续的内存碎片。后期分配大对象时找不到足够连续的内存则会触发GC

2.复制算法(现代商业虚拟机用来回收新生代)

为解决效率问题。

思想:将可用内存一分为二,每次只使用其中一块,当A用完就把A所有存活对象赋值到B上,然后清空A。但是研究表明绝大多数对象都是朝生夕灭,因此A:B=1:1不合理,采取的是A:B=8:1:1.即前面提到的Eden和survivor方式。

3.标记整理(针对老年代)

根据老年代对象存活时间长的特点。

  • 阶段一:标记需要回收的对象
  • 阶段二:整理。让所有存活对象都向另一端移动,再直接清理掉边界以外的内存。

4.分代收集。分代收集只是针对新生代和老年代采取不同的算法,但还是上面的这三种

现代虚拟机都采用的分代收集。并没有新的思想,只是对新生代采用复制算法,对老年代采用标记整理或者标记清除算法

问题:新生代eden survivor区的比例,为什么是这个比例,eden survivor的工作过程?

首先hotspot虚拟机对eden survivor是对新生代的内存划分方式,如下图:

eden区域占据8份,两个survivor区域各占1份。大多数的新生代都是采用的复制清除法作为垃圾回收算法。当对新生代进行minor gc时,会把Eden中和Survivor from中的存活对象复制到另一块survivor to的区域中。

因为新生代中98%的对象都是"朝生夕死"的,只有很少会存活下来,因此就设定了10%的空间来存放活下来的。但是万一还是出现了不止10%的对象存活下来呢?岂不是放不下了?虚拟机考虑到了这个情况并有一个分配担保机制:在这种情况下让它们直接进入老年代即可。从新生代晋升到老年代有以下三种方式:

  • 有一个年龄参数MaxTenuringThreshold用来判断,每次对象熬过一次GC,年龄加1,当到达设定的阈值时,可以进入老年代。
  • 比较大的对象(需要大量连续的内存空间),同样,虚拟机也提供了一个参数PretenureSizeThreshold,默认15,可以设置这个值,当对象大于设置值可以直接进入老年代。
  • 如果Survivor空间中相同年龄的对象大小总和大于Survivor空间的一半(即上述情况),则年龄大于或等于该年龄的对象就可以进入老年代。这里是属于动态判定,适应那些内存较小的情况

那么基于比较常见的第一种方式,为什么我们需要两块survivor区域呢,如果不划分两块survivor区域会有什么问题吗?如果只有一个Survivor区,GC时Eden区的可以进入Survivor区,那Survivor区的去到哪里呢?虽然在每一次gc时可能会有对象存活下来,但是这些存活下来的对象并不一定会晋升,这意味着每次gc时可能survivor区中都有一些已经在里面的对象,如果只有一个survivor区的话,对于这个区域的处理就会非常麻烦。而如果有两个survivor区的话,则只需将eden区中的存活对象,和一个survivor区中的存活的像,都拷贝到另一个survivor区中,然后再直接清除eden区和survivor from区即可。

这里很有意思的一点是,survivor from和 survivor to之间应是不停互换角色的

问题:是否可以GC直接内存?

首先需要明确的是,直接内存是指什么。

直接内存,实际上就是系统物理内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是Java虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用,而且也可能导致OutOfMemoryError异常出现。

在JDK 1.4中新加入了NIO(New Input/Output)类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。

直接内存的分配不受java堆大小限制,但是必然会受到本机内存大小等的限制。

因此不可GC直接内存?问题 虚拟机gc native stack的时候会回收直接内存吗?

HotSpot的算法实现

上面介绍的内容是垃圾回收算法的理论知识。那么作为商业虚拟机的HotSpot是怎么实现垃圾回收的呢?

在实际的项目当中,会面临这样一些问题:方法区的内容可能会非常多,引用数量庞大。且每时每刻,一个运行着的java程序的引用情况都是在发生变化的,因此在进行可达性分析的时候,对象引用的关系在某个时间段上,必须是保持不变的,这一点是导致进行GC进行时必须停顿所有java执行线程的一个重要原因——Sun将这件事称为"Stop the world".

Hotspot虚拟机中使用一组称为OopMap的数据结构来维护哪些地方存放着对象引用。

关于安全点与安全区域

在OopMap的协助下,Hotspot虚拟机可以快速且准确地完成GC Roots枚举,但是选择恰当的时机进行GC仍然非常重要,这些特定的GC位置被称为安全点SafePoint。

Safepoint的特点是:是否具有让程序长时间执行的特点。比较典型的如方法调用,循环跳转等都是safepoint的选择。

除了安全点之外还有安全区域。所谓安全区域就是指线程进入到某个区域时就可以被JVM的GC直接忽略。典型的应用场景是线程处于Sleep或者Blocked状态的场景。

常用的JVM调优参数

GC 命令行选项 描述
-Xms 设置Java堆大小的初始值/最小值。例如:-Xms512m (请注意这里没有”=”).
-Xmx 设置Java堆大小的最大值
-Xmn 设置年轻代对空间的初始值,最小值和最大值。请注意,年老代堆空间大小是依赖于年轻代堆空间大小的
-XX:PermSize=<n>[g|m|k] 设置持久代堆空间的初始值和最小值
-XX:MaxPermSize=<n>[g|m|k] 设置持久代堆空间的最大值

dump文件分析

老年代什么情况下会发生gc?

除直接调用System.gc外,触发Full GC执行的情况有如下四种。

1. 老年代空间不足。

老年代代空间只有在新生代对象转入及创建为大对象、大数组时才会出现不足的现象,当执行Full GC后空间仍然不足,则抛出如下错误: java.lang.OutOfMemoryError: Java heap space 为避免以上两种状况引起的FullGC,调优时应尽量做到让对象在Minor GC阶段被回收、让对象在新生代多存活一段时间及不要创建过大的对象及数组。

2. Permanet Generation空间满

PermanetGeneration中存放的为一些class的信息等,当系统中要加载的类、反射的类和调用的方法较多时,Permanet Generation可能会被占满,在未配置为采用CMS GC的情况下会执行Full GC。如果经过Full GC仍然回收不了,那么JVM会抛出如下错误信息: java.lang.OutOfMemoryError: PermGen space 为避免Perm Gen占满造成Full GC现象,可采用的方法为增大Perm Gen空间或转为使用CMS GC。

3. CMS GC时出现promotion failed和concurrent mode failure

对于采用CMS进行旧生代GC的程序而言,尤其要注意GC日志中是否有promotion failed和concurrent mode failure两种状况,当这两种状况出现时可能会触发Full GC。 promotionfailed是在进行Minor GC时,survivor space放不下、对象只能放入旧生代,而此时旧生代也放不下造成的;concurrent mode failure是在执行CMS GC的过程中同时有对象要放入旧生代,而此时旧生代空间不足造成的。 应对措施为:增大survivorspace、旧生代空间或调低触发并发GC的比率,但在JDK 5.0+、6.0+的版本中有可能会由于JDK的bug29导致CMS在remark完毕后很久才触发sweeping动作。对于这种状况,可通过设置-XX:CMSMaxAbortablePrecleanTime=5(单位为ms)来避免。

4. 统计得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间

这是一个较为复杂的触发情况,Hotspot为了避免由于新生代对象晋升到旧生代导致旧生代空间不足的现象,在进行Minor GC时,做了一个判断,如果之前统计所得到的Minor GC晋升到旧生代的平均大小大于旧生代的剩余空间,那么就直接触发Full GC。 例如程序第一次触发MinorGC后,有6MB的对象晋升到旧生代,那么当下一次Minor GC发生时,首先检查旧生代的剩余空间是否大于6MB,如果小于6MB,则执行Full GC。 当新生代采用PSGC时,方式稍有不同,PS GC是在Minor GC后也会检查,例如上面的例子中第一次Minor GC后,PS GC会检查此时旧生代的剩余空间是否大于6MB,如小于,则触发对旧生代的回收。 除了以上4种状况外,对于使用RMI来进行RPC或管理的Sun JDK应用而言,默认情况下会一小时执行一次Full GC。可通过在启动时通过- java-Dsun.rmi.dgc.client.gcInterval=3600000来设置Full GC执行的间隔时间或通过-XX:+ DisableExplicitGC来禁止RMI调用System.gc。

场景分析:一台4核8g的服务器,每隔两小时就要出现一次老年代gc,现在有日志,怎么分析是哪里出了问题

java有没有主动触发GC的方式?

我们可以在代码里调用:

System.gc();Runtime.getRuntime().gc();java.lang.management.MemoryMXBean.gc()

但这些方法的作用只是告诉JVM尽快GC一次,不会立即执行GC。虚拟机的规范是通知虚拟机尽快执行,没有强制规定执行时间,因此按照这个规范,答案是没有。

java内存模型与线程, happens before原则,内存模型与前面的内存区域的区别?

happens-before是JMM的核心。jmm即java memory model也就是java内存模型。java内存模型的用意是来屏蔽掉各种硬件和操作系统的内存访问差异,以实现让java程序在各种平台下都能达到一致饿内存访问效果。在jdk1.5之后成熟和完善了起来。

主内存与工作内存

  • JMM规定所有的变量存储在主内存(注意只是实例字段,静态字段,不包括局部变量,方法参数,因为后者是线程私有的不会被共享也就不会存在竞争问题。如果局部变量是一个reference类型,它引用的对象在java堆中被各个线程共享,但是reference这个引用本身在java栈的局部变量表中,它是线程私有的)
  • 每条线程有自己的工作内存(保存该线程用到的变量的主内存副本拷贝)
  • 线程对变量的所有操作 (读取,赋值等)都必须在工作内存中进行,而不能直接读写主内存中的变量。问题:volatile关键字违反这一规则吗?答案:不违反,volatile变量仍然有工作内存的拷贝,但是由于它特殊的操作顺序规定,使得它看起来如果直接在主内存中读写访问一般,因此它并不是例外
  • 不同的线程之间无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存存完成

线程,主内存,工作内存三者的交互关系如图:

与java内存区域的区别

这里所说的主内存,工作内存与之前的java内存区域中的java堆,栈,方法区等并不是同一个层次的划分,两者基本上没有关系。如果一定要对应起来,则从定义上来看,主内存主要对应于java堆上的对象实例数据部分(对象除了实例数据部分还有对象头的信息),工作内存则对应与虚拟机栈中的部分区域。

happens-before原则

也叫先行发生原则,我们在写java并发代码时并没有感觉。下面是happens-before原则规则:

  1. 程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作;
  2. 锁定规则:一个unLock操作先行发生于后面对同一个锁额lock操作;
  3. volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作;
  4. 传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C;
  5. 线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作;
  6. 线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生;
  7. 线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行;
  8. 对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始;

jvm的优化

设置参数,设置jvm的最大内存数

垃圾回收器的选择

虚拟机类加载机制及类加载执行子系统

关于这一部分的内容,笔者单独写了一篇博客[JVM]虚拟机类加载机制

[JVM] java虚拟机内存管理机制及垃圾收集相关推荐

  1. Java 虚拟机内存分配机制

    Java 虚拟机内存分配机制 内存区域划分 对于大多数的程序员来说,Java 内存比较流行的说法便是堆和栈,这其实是非常粗略的一种划分,这种划分的"堆"对应内存模型的 Java 堆 ...

  2. Java 的内存管理机制是怎样的?

    Java 的内存管理机制是怎样的?

  3. 十分良心!全网最详细的Java 自动内存管理机制及性能优化教程

    同样的,先来个思维导图预览一下本文结构. 一图带你看完本文 一.运行时数据区域 首先来看看Java虚拟机所管理的内存包括哪些区域,就像我们要了解一个房子,我们得先知道这个房子大体构造.根据<Ja ...

  4. java 虚拟机内存管理_java虚拟机内存管理

    程序计数器: 程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器. 程序计数器处于线程独占区 如果线程执行的是Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地 ...

  5. java的内存管理机制

    1.内存区域的分类 栈内存:基本类型变量和对象的引用,优势在于存取速度快 堆内存:new创建的对象和数组以及对象的实例化变量,优势在于动态分配内存,但是存取速度相对较慢 2.不同类型的内存分配 (1) ...

  6. 内存分段分页机制理解_深入理解虚拟机,JVM高级特性-自动内存管理机制

    什么是自动内存管理机制? 对于java程序员来说,有一点是要比C/C++程序员要方便的,那就是程序在运行时,java程序不需要为每一个对象其编写对应的释放内存的代码,JVM虚拟机将为你在合适的时间去释 ...

  7. JVM自动内存管理机制——Java内存区域(下)

    一.虚拟机参数配置 在上一篇<Java自动内存管理机制--Java内存区域(上)>中介绍了有关的基础知识,这一篇主要是通过一些示例来了解有关虚拟机参数的配置. 1.Java堆参数设置 a) ...

  8. 浅析java内存管理机制

    内存管理是计算机编程中的一个重要问题,一般来说,内存管理主要包括内存分配和内存回收两个部分.不同的编程语言有不同的内存管理机制,本文在对比C++和java语言内存管理机制的不同的基础上,浅析java中 ...

  9. 1、虚拟机内存管理、运行时数据区、线程共享区、Java堆、新生代、老年代、Eden区域分配、方法区、线程独占区、虚拟机栈

    1.Java虚拟机内存管理 1.1.运行时数据区[Runtime Data Area] 1.1.1.线程共享区 1.1.1.1.Java堆[heap] 1.1.1.1.1.新生代.老年代.Eden区域 ...

最新文章

  1. 独家 | 教你用Pytorch建立你的第一个文本分类模型!
  2. 编程笔试(解析及代码实现):从矩阵中寻找和最大的子矩阵(首先需要将一个列表转为一个方矩阵)
  3. DAVINCI DM365-368中 linux-2.6.32的移植
  4. 游戏用户体验指标_电子游戏如何超越游戏化的用户体验
  5. 用户画像是怎么生成出来的?
  6. Java知多少(56)线程模型
  7. 基于javaweb+mysql的在线购书系统网上书店图书商城(前台、后台)(java+Springboot+ssm+mysql+maven)
  8. uniapp创建电子签名
  9. numpy的choose 函数实现条件筛选
  10. 笔记本电脑WIN10开热点手机无法连接的问题
  11. 中南林业科技大学Java实验报告八:包与接口
  12. 怎么将抖音上的多个视频合成一个视频
  13. php面试时的自我称呼,PHP程序员面试自我介绍
  14. Python pandas 实现Excel分列效果
  15. dva的用法_dva 的基本用法
  16. 使用两个无线路由器实现桥接中继
  17. 【高等数学】函数连续、可导、可微,洛必达法则使用条件、一阶可导、一阶连续可导、二阶可导、二阶连续可导
  18. elasticsearch2.1部署
  19. PRML笔记:1-介绍
  20. echarts中的zlevel和z的区别

热门文章

  1. 交友网站Circl.es帮你找到另一半
  2. 联想财报里的伏笔:智能化制造迎来新一轮发展契机
  3. 通过按钮跳过SplashActivity(启动画面),延时自动跳过SplashActivity进入MainActivity
  4. 区块链开发+跨境支付模式解决电商支付方式的痛点
  5. 推荐6 款超实用app软件工具,值得收藏!
  6. 二进制补码计算原理详解
  7. 二进制补码转为十进制
  8. 名字空间(namespace)
  9. pwnable.krToddlr’s Bottle题解3
  10. HP Z620 Windows 7 系统安装(含磁盘阵列)