深入理解java虚拟机——自动内存管理

目录

第一章 java内存区域与内存溢出异常

1.运行时数据区域

1.1程序计数器

1.2 java虚拟机栈

1.3本地方法栈

1.4堆区域

1.5方法区

1.6 运行时常量池

2.hotspot虚拟机对象探秘

2.1对象的创建

2.2对象的内存布局

2.3对象的访问定位

3.jvm相关异常

3.1内存溢出与内存泄露的区别

3.2jvm各区域溢出


第一章 java内存区域与内存溢出异常

1.运行时数据区域

深入理解java虚拟机——自动内存管理_第1张图片

绿色的是所有线程共享的区域

橙色的是线程独有的

1.1程序计数器

程序计数器是一块较小的内存空间,用来存放下一条需要执行的字节码指令,它是控制程序流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖它来完成。

jvm的多线程是通过线程轮流切换、分配处理器执行时间的方式实现的,因为某一时刻,一个处理器都只会执行一条线程中的指令

1.2 java虚拟机栈

虚拟机栈是描述Java的方法执行的内存区域,每个方法被执行时都会同时创建一个栈帧,用于存放局部变量表,操作栈,动态链接、方法出口信息等,每一个方法被调用直至程序执行完成的过程,就是对应一个栈帧在虚拟机战马的出栈的过程。

特点:

● 内部结构是栈帧,每个方法在执行的时候都会创建一个栈帧,用于存储局部变量表,操作数栈,动态链接,方法返回地址等信息

● 某方法在调用另一个方法是通过动态链接在常量池中查询方法的引用,进而完成方法调用

● 某方法在调用另一个方法的过程,即是一个栈帧在虚拟机中的入栈到出栈的过程

● 虚拟机中的方法入栈的顺序和方法的调用顺序是一致的

深入理解java虚拟机——自动内存管理_第2张图片

局部变量表

主要存放了编译器可知的各种数据类型(boolean、byte、char、short、int、float、long、double)、对象引用(reference类型,它不同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)。

其中64 位长度的long 和double 类型的数据会占用2 个局部变量空间(Slot),其余的数据类型只占用1 个。局部变量表所需的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在帧中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。

详细的虚拟机栈解读请查看下图,一目了然:

深入理解java虚拟机——自动内存管理_第3张图片

Java 虚拟机栈会出现两种异常:StackOverFlowError 和 OutOfMemoryError。

StackOverFlowError:

若Java虚拟机栈的内存大小不允许动态扩展,当线程请求栈的深度超过当前Java虚拟机栈的最大深度的时候,就抛出StackOverFlowError异常。

OutOfMemoryError:

若Java虚拟机栈的内存大小允许动态扩展,且当线程请求栈时内存用完了,无法再动态扩展了,此时抛出OutOfMemoryError异常。

Java 虚拟机栈也是线程私有的,每个线程都有各自的Java虚拟机栈,而且随着线程的创建而创建,随着线程的死亡而死亡。

1.3本地方法栈

本地方法栈是指非java语言编写的,有jvm调用(可与底层操作系统交互)

和虚拟机栈所发挥的作用非常相似。

区别:虚拟机栈为虚拟机执行 Java 方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。

本地方法被执行的时候,在本地方法栈也会创建一个栈帧,用于存放该本地方法的局部变量表、操作数栈、动态链接、出口信息。方法执行完毕后相应的栈帧也会出栈并释放内存空间,也会出现 StackOverFlowError 和 OutOfMemoryError 两种异常。

1.4堆区域

Java 虚拟机所管理的内存中最大的一块, Java 堆是所有线程共享的一块内存区域,在虚拟机启动时创建。此内存区域的唯一目的就是存放对象实例,几乎所有的对象实例以及数组都在这里分配内存。

根据Java 虚拟机规范的规定,Java 堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,就像我们的磁盘空间一样。在实现时,既可以实现成固定大小的,也可以是可扩展的,不过当前主流的虚拟机都是按照可扩展来实现的(通过-Xmx和-Xms 控制)。如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError 异常。

特点:

● 存放 Java 对象和数组

● 虚拟机中存储空间比较大的区域

● 可能出现 OOM 异常区域

● 该区域是 GC 的主要区域,堆区由年轻代和老年代组成,年轻代又分为 Eden 区、S0区(from survivor)、S1 区(to survivor);新生代对应 Minor GC(Young GC),老年代对应 Full GC(Old GC)。(垃圾回收部分后面会说)

Java 堆是垃圾收集器管理的主要区域,因此也被称作GC堆(Garbage Collected Heap).从垃圾回收的角度,由于现在收集器基本都采用分代垃圾收集算法,所以Java堆还可以细分为:新生代和老年代:

在细致一点有:Eden空间、From Survivor、To Survivor空间等。进一步划分的目的是更好地回收内存,或者更快地分配内存。

深入理解java虚拟机——自动内存管理_第4张图片

(1) 堆是JVM中所有线程共享的,因此在其上进行对象内存的分配均需要进行加锁,这也导致了new对象的开销是比较大的

(2) Sun Hotspot JVM为了提升对象内存分配的效率,对于所创建的线程都会分配一块独立的空间TLAB(Thread Local Allocation Buffer),其大小由JVM根据运行的情况计算而得,在TLAB上分配对象时不需要加锁,因此JVM在给线程的对象分配内存时会尽量的在TLAB上分配,在这种情况下JVM中分配对象内存的性能和C基本是一样高效的,但如果对象过大的话则仍然是直接使用堆空间分配

(3) TLAB仅作用于新生代的Eden Space,因此在编写Java程序时,通常多个小的对象比大的对象分配起来更加高效。

(4) 所有新创建的Object 都将会存储在新生代Yong Generation中。如果Young Generation的数据在一次或多次GC后存活下来,那么将被转移到OldGeneration。新的Object总是创建在Eden Space。

1.5方法区

方法区与 Java 堆一样,是各个线程共享的内存区域,它用于类的信息(名称、修饰符,字段、方法、接口等)、类中的静态变量、类中定义为final类型的常量、类中的Field信息、类中的方法信息,当开发人员在程序中通过Class对象中的getName、isInterface等方法来获取信息时,这些数据都来源于方法区域,同时方法区域也是全局共享的,在一定的条件下它也会被GC,当方法区域需要使用的内存超过其允许的大小时,会抛出OutOfMemory的错误信息。

1.6 运行时常量池

是方法区中的一部分(常量池表)

运行时常量池(Runtime Constant Pool):存放的为类中的固定的常量信息、方法和Field的引用信息等,其空间从方法区域中分配。

例如:String类的intern()方法(jkd1.7之后单独划分出来字符串常量池在堆中

2.hotspot虚拟机对象探秘

2.1对象的创建

  1. 类加载
  2. 分配内存
  3. 初始化值
  4. 为对对象进行必要设置
  5. 执⾏ init ⽅法

类加载:

当jvm收到一条new相关的指令时,首先会先去检查这个指令的参数是否能够在常量池中定位到一个类的符号引用,并检查这个类是否有被加载、解析过,如果没有会先执行相应的类加载。

分配内存:

jvm接下来将会为新生对象分配内存,内存大小在类加载完成之后变可确定,为对象分配空间便等同于把一块确定大小的内存块从java堆中划分出来。

有两种分配方式:(具体内容在垃圾回收器章节)

1.指针碰撞:所有被使用过的内存都被放在一边,空闲的内存被放在另一边,中间放着一个指针作为分解点的指示器,那所分配的内存仅仅只是把指针移动一段与对象内存大小相等的空间出来即可。

2.空闲列表:记录这哪些内存块时可用的,再分配时候就从列表中找出一块足够大的空间划分给对象实例

并发情况下分配内存空间的处理方式:

1.对内存分配空间的动作做同步处理,实际上虚拟机是采用CAS搭配上失败重试的方式保证更新操作的原子性

2.把内存分配的动作按照线程划分在不同的空间中进行,即每个线程在java堆中都存在一小块内存,成为本地线程分配缓存(Thread Local Allocation Buffer ,TLAB),哪个线程需要分配内存,就在该线程的本地缓存区进行。只有当本地线程分配缓存内存全用完了,需要分配心的缓存区内存时,才需要同步锁定

初始化值:

jvm必须将分配到的空间(不包含对象头)都初始化为零值,若采用的是tlab的话,这个部分工作早tlab分配是同时进行了

为对对象进行必要设置:

例如这个对象是哪个类的实例、如果才能找到类的元数据信息、对象哈希码、对象的GC分代年龄信息,以及是否启用偏向锁(对象头中会记录线程信息)【针对同一线程多次访问同一段代码块可以考虑采用偏向锁】

执⾏ init ⽅法:

​ 从虚拟机的视⻆来看,⼀个新的对象已经产⽣了,但从Java 程序的视⻆来看, ⽅法还没有执⾏,所有的字段都还为零。所以⼀般来说(除循环依赖),执⾏ new 指令之后会接着执⾏ ⽅法,这样⼀个真正可⽤的对象才算产⽣出来。

2.2对象的内存布局

对象在堆内存中存储布局可以划分三个部分:对象头、实例数据、对齐填充

对象头:

  1. 自身运行数据
  2. 类型指针

hostspot虚拟机对象中对象头部分包含两类信息,一类是用于存储对象自身运行时数据,如hashcode,GC分代年龄、锁状态标志、线程持有的锁,偏向锁的ID、偏向锁时间戳。另一类是类型指针,即对象指向它的类型元数据的指针(类的信息存放在方法区中【“非堆”】)。

实例数据:

是真正存放存储的有效信息

对齐填充:

对象实例数据部分没有对齐的部分自动补齐(8字节倍数)

2.3对象的访问定位

2.3.1句柄访问对象

如果使用句柄访问的话,Java堆中将可能会划分出一块内存来作为句柄池,reference中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自具体的地址信息

深入理解java虚拟机——自动内存管理_第5张图片

2.3.2直接指针访问对象

如果使用直接指针访问的话,Java堆中对象的内存布局就必须考虑如何放置访问类型数据的相关 信息,reference中存储的直接就是对象地址,如果只是访问对象本身的话,就不需要多一次间接访问 的开销

深入理解java虚拟机——自动内存管理_第6张图片

这两种对象访问方式各有优势,使用句柄来访问的最大好处就是reference中存储的是稳定句柄地 址,在对象被移动(垃圾收集时移动对象是非常普遍的行为)时只会改变句柄中的实例数据指针,而 reference本身不需要被修改。使用直接指针来访问最大的好处就是速度更快,它节省了一次指针定位的时间开销。

2.4对象的引用

普通的对象引用关系就是强引用

例如:Dog dog =new Dog();

软引用用于维护一些可有可无的对象。只有在内存不足时,系统则会回收软引用对象,如果回收了软引用对象之后仍然没有足够的内存,才会抛出内存溢出异常。

例如:Dog dog =new SoftReference(new Dog());

弱引用对象相比软引用来说,要更加无用一些,它拥有更短的生命周期,当 JVM 进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。

例如: Dog dog =new WeakReference(new Dog());

虚引用是一种形同虚设的引用,在现实场景中用的不是很多,它主要用来跟踪对象被垃圾回收的活动。

例如:

        String str =new String("疯狂Java讲义");
        ReferenceQueue rq=new ReferenceQueue();
        WeakReference wr=new WeakReference(str);
        PhantomReference pr=new PhantomReference(str,rq)
 

3.jvm相关异常

jvm的异常主要有:OutOfMemoryError(oom 内存溢出)、stackOverFlowError(超过最大调用堆栈深度)

3.1内存溢出与内存泄露的区别

内存溢出(out of memory),是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。

内存泄露(memory leak),是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。

memory leak会最终会导致out of memory!

内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出。

内存泄漏是指你向系统申请分配内存进行使用(new),可是使用完了以后却不归还(delete),结果你申请到的那块内存你自己也不能再访问(也许你把它的地址给弄丢了),而系统也不能再次将它分配给需要的程序。

3.2jvm各区域溢出

Java堆溢出(堆参数 -Xmx与-Xms)

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

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

hotstop提供了一些参数作为元空间的防御措施

-XX:MaxMetaspaceSize:设置元空间最大值,默认是-1,不限制,受本地内存大小限制

-XX:MetaspaceSize:指定元空间的初始空间大小,以字节为单位,达到该值就会触发垃圾收集进行类型卸载,同时收集器会对该值调整

-XX:MinMetaspaceFreeRatio:作用是在垃圾收集之后控制最小的元空间剩余容量的百分比,可减少因元空间不足导致的垃圾收集的频率

本机内存溢出

你可能感兴趣的:(java,jvm,开发语言)