图解JVM(二)

一 内存模型

java内存数据模型的介绍

java程序在运行的时候会在内存中开辟不同的空间用以管理不用的内存区域,每个区域都有自己的功能,创建和销毁时间,有的区域会随着虚拟机的启动而创建,而有的区域会随着用户线程的创建而销毁。按照空间,分为如下不同的空间,接下来我们将按照不同的区域进行学习,学习它不同区域的功能和用法
在这里插入图片描述
二:线程私有内存

我们先来看一下多线程运行原理:其实cpu在运行的时候每次在同一特定的时间点只能运行一个线程(单核的情况下),只不过它切换的速度非常之快,让我们觉的它是在进行多线程运行,本质上它还是单一运行的。所以这就会必然引出一个问题:cpu切换线程如何保证它自身的运行不受其他的线程影响,保证每个线程都是独立不受外部侵扰的,这就产生了线程私有内存这个概念,主要是维持线程的安全、稳定、高效的运转。关于这一点也很好理解,比如我们去一个快速餐厅吃饭,我们点好了菜,会产生菜单小票,这个小票上面有编号,就是每个线程的“私有内存”,服务员再进行不同顾客上菜的时候就有了区分度,这样就可以顺利找到不同的顾客(切换不同的线程);在java线程中,程序计数器、虚拟机栈、本地方法栈都属于线程私有的。

注意点:线程私有不存在多线程并发的资源竞争问题,因为其享有的内存是互不影响的,不存在并发问题

与线程私有相对,就有线程公有内存,这个区域在jvm虚拟机中有个特定的称呼叫做:主内存

在这里插入图片描述
二 JAVA堆

Java堆主要是存储对象的地方,同时它也是java内存管理区域最大的一块。当我们在程序中new一个对象出来或者新建一个数组,就会把对象存储这个区域。它是java的最重要的公共区域之一,与线程私有相对,它是属于线程公有的。基本上所有的对象都会存储在此区域,但也不是绝对的(随着JIT技术的成熟,这一点会发生微变)。堆是主要存放对象的地方,同时它就会产生另一个问题就是GC(Garbage Collection),当垃圾进行回收的时候,虚拟机并没有开辟新的空间,还在此区域进行,同时这个区域也叫“GC堆”,再细致划分下去,分为新生代、老年代,同时还分为Eden空间、From survivor空间、To Survior区域。为什么要划分的这么细致呢?其实是为了更加有效率的回收,划分的区域越细致,那么垃圾回收器收集的时候只要去对应的地方直接回收就行,不用加上额外的判断逻辑。
1:堆在逻辑上是连续不间断的内存空间,但是在物理上可以是不连续的内存空间

2:在堆中如果没有完成内存的实例分配就会抛出oom

3:java堆是线程公有的,所有的线程共享这一片区域
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
3 JAVA虚拟机栈
虚拟机栈主要是作用在java方法运行时候,每个线程在运行到一个方法的入口都会创建一个栈帧,创建栈帧的目的在于存储局部变量、操作数栈、动态链接、方法出口等信息。按照这个原理,那么一个方法在从调用直到完全执行完毕,都会对应一个从入栈到出栈的过程。

这里我们说明一下局部变量,它的含义就是定义在java方法内部中的变量,对应于java中8大基本数据类型,byte、int、float、boolean、char、short、long、reference:(long和double占用2个空间,其余1个)局部变量表里我们需要注意的是,它在编译期已经完全了内存分配,这样栈帧在用局部变量的时候,它占用的内存是已经确认的,不需要再分配,这就一定程度上减少了栈帧的工作量。

注意:

1:当线程请求的栈帧的深度大于虚拟机允许的深度,就会抛出stackOverFlow异常

2:我们平时所说的堆栈,其中的栈就是指的是这里的栈帧

3:它也是线程私有的,并且和线程的生命周期相同。

在这里插入图片描述在这里插入图片描述
在这里插入图片描述
4 本地方法栈
本地方法栈,顾名思义它代表的就是本地方法Native执行的栈帧,在jdk的源码中我们可以看到很多命名为Native的方法,Native方法很大一部分是采用C/C++写的,但是它对Native采用的数据结构、语言等都没有做具体的要求,这一点完全是由java虚拟机进行实现的。

注意:

1:这个区域会抛出oom或者sof(stackoverflowError)异常

2:同样它属于本地线程私有的
在这里插入图片描述
简介
十分类似Java虚拟机栈,与Java虚拟机区别在于:服务对象,即Java虚拟机栈为执行 Java方法服务;本地方法栈为执行 Native方法服务
5 方法区
方法区主要是用来存放已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,比如我们在代码中定义的Constant常量就会在这个区域存储。在java虚拟机规范中,它是属于堆的逻辑部分。同时在这个区域中,它也会有垃圾回收器工作,这个区域叫做“永久代”,之所以叫做永久代,因为它比新生代和老年代拥有更长的生命周期,但是并不是在这个区域它就会万事大吉了,永久代依然会存在垃圾回收的情况,只不过相对来说较少
1:此区域属于线程公有的,线程的类信息、常量、静态变量、编译代码都在此区域进行存储

2:此区域会抛出oom异常,发生在方法区无法进行内存分配时
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
其内部包含一个运行时常量池,具体介绍如下
在这里插入图片描述运行时常量池:

运行时常量池主要是方法区的一部分,class文件除了有类的版本、字段、方法接口等描述信息外,还有一项信息是常量池,用于存储编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区运行时常量池中存放。

注意:此区域同样会发生oom异常
6 程序计数器
对应于图上标记的深红色部分,主要是指程序在运行过程中所执行的字节码(.class)文件的行号指示,也称作行号指示器,程序流程的分支、循环、异常处理等基础功能都需要它的引导来完成。它占有的内存空间比较小,它的运行的原理是:通过改变计数器的值来选取下一条的需要执行的字节码命令,而这个值具体指的是虚拟机在Java方法中字节码指令的地址,但是如果执行的是Native方法,那么它的计数器的值是为空的。

1:它是java中唯一一个没有内存溢出(out of memory)情况的区域。可以思考一下这点是为什么?它是进行线程运行的指示灯,如果没有了它,程序也就无法运行了。

2:它属于线程私有的:每个线程内部都有一个程序计数器,为了保证每个线程切换前后都能正常运行。
在这里插入图片描述
在这里插入图片描述
7 直接内存
定义:NIO类(JDK1.4引入)中基于通道和缓冲区的I/O方式 通过使用Native函数库 直接分配 的堆外内存
特点:不受堆大小限制
应用场景:适用于频繁调用的场景
通过一个 存储在Java堆中的DirectByteBuffer对象 作为这块内存的引用 进行操作,从而避免在Java 堆和 Native堆之间来回复制数据,提高使用性能
抛出的异常:OutOfMemoryError,即与其他内存区域的总和 大于 物理内存限制
本文全面讲解JVM中的内存模型 & 分区,总结如下
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值