JVM基础知识

内存管理

java虚拟机运行时数据区

在这里插入图片描述

  • 程序计数器

多核处理器,多线程切换后,如何恢复到执行位置,就是通过程序计数器。每个线程都有一个独立的程序计数器,用于标志执行位置,属于线程私有,因此不会出现内存溢出的情况。

  • java虚拟机栈

每个方法执行都会创建一个帧栈,用于存储局部变量、操作数栈、动态连接、方法出口等。每个方法执行时与执行完成时,对应这这个帧栈在java虚拟机栈的入栈与出栈。如果方法过多,超了虚拟机允许的栈深度,就会抛出StackOverflowError。

  • 本地方法栈

与java虚拟机栈类似,只是服务于本地方法

  • java堆

java堆是垃圾收集器管理的内存区域,也是GC堆,一般划分新生代、老年代、永久代等。它的目的就是存放对象,为对象实例分配内存。如果堆内存不够了,无法分配内存,会抛出outOfmemoryError 可以通过参数-Xms与-Xmx来设置堆大小。

  • 方法区

这与java堆一样,是线程共享内存区域,用于存储被已虚拟机加载的类型信息、常量、静态变量等。也有称作永久代,方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量,垃圾收回一般不会回收这个。

  • 运行时常量池

是方法区的一部分,用于存放编译期生成的各种字面量与符号引用。

  • 直接内存

NIO的Buffer提供了一个可以不经过JVM内存直接访问系统物理内存的类——DirectBuffer。 DirectBuffer类继承自ByteBuffer,但和普通的ByteBuffer不同,普通的ByteBuffer仍在JVM堆上分配内存,其最大内存受到最大堆内存的限制;而DirectBuffer直接分配在物理内存中,并不占用堆空间,其可申请的最大内存受操作系统限制。直接内存的读写操作比普通Buffer快,但它的创建、销毁比普通Buffer慢。

JAVA的JVM的内存可分为3个区:堆(heap)、栈(stack)和方法区(method)
栈区:

一个线程对应一个栈

  • 1、每个线程包含一个栈区,栈中只保存方法中(不包括对象的成员变量)的基础数据类型和自定义对象的引用(不是对象),对象都存放在堆区中
  • 2、每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。
  • 3、栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。
    https://www.cnblogs.com/wade-luffy/p/5753057.html
堆区:
  • 1、存储的全部是对象实例,每个对象都包含一个与之对应的class的信息(class信息存放在方法区)
  • 2、jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身,几乎所有的对象实例和数组都在堆中分配。
方法区:
  • 1、又叫静态区,跟堆一样,被所有的线程共享。它用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据
  • 永久代和方法区的关系:方法区是Java虚拟机规范中的定义,是一种规范,而永久代是一种实现,一个是标准一个是实现
  • 元空间:对于Java8, HotSpots取消了永久代,那么是不是也就没有方法区了呢?当然不是,方法区是一个规范,规范没变,它就一直在。那么取代永久代的就是元空间。它可永久代有什么不同的?存储位置不同,永久代物理是是堆的一部分,和新生代,老年代地址是连续的,而元空间属于本地内存;存储内容不同,元空间存储类的元信息,静态变量和常量池等并入堆中。相当于永久代的数据被分到了堆和元空间中

堆内存设置

-Xms:初始堆大小
-Xmx:最大堆大小
-XX:NewSize=n:设置年轻代大小
-XX:NewRatio=n:设置年轻代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
-XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5
-XX:MaxPermSize=n:设置持久代大小

垃圾回收

如何判断对象是否存活

  • 计数引用算法

给对象添加一个引用计数器,每当有一个地方引用它,计数器值就加一;相反的,当引用失效的时候,计数器值就减一;任何时刻计数器为0的对象就是不可能再被使用的

  • 可达性分析算法

通过一系列的称谓“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路称为引用链,当一个对象到GC Roots没有任何引用链相连的时候,(用图论的话来说)即从GC Roots到这个对象不可达,则证明这个对象是不可用的

垃圾回收算法

名称标记-清除算法复制算法标记-整理算法分代收集算法
速度高效最慢较好
空间开销很少(但会有空间碎片)通常需要存活对象的2倍大小(不堆积碎片)很少(不会有空间碎片)一般(较少有空间碎片)
移动对象

分代收集算法

  • 通俗理解

我是一个普通的java对象,我出生在Eden区,在Eden区我还看到和我长的很像的小兄弟,我们在Eden区中玩了挺长时间。有一天Eden区中的人实在是太多了,我就被迫去了Survivor区的“From”区,自从去了Survivor区,我就开始漂了,有时候在Survivor的“From”区,有时候在Survivor的“To”区,居无定所。直到我18岁的时候,爸爸说我成人了,该去社会上闯闯了。于是我就去了年老代那边,年老代里,人很多,并且年龄都挺大的,我在这里也认识了很多人。在年老代里,我生活了20年(每次GC加一岁),然后被回收

  • 堆内存划分

年轻代(伊甸园、from区、to区)、老年代
> 年前代:![在这里插入图片描述](https://img-blog.csdnimg.cn/2020120716173444.png

参考文章

  • 使用的算法
  • 伊甸园:复制算法
  • from、to区:标记整理算法
  • 老年代:标记-清除算法

虚拟机执行

虚拟机的语言无关性

在这里插入图片描述

class类文件结构

在这里插入图片描述
更多详情看

虚拟机类文件加载过程

在这里插入图片描述
参考更多

编译方式

常见方式
  • 前端编译

把Java源码文件(.java)编译成Class文件(.class)的过程;
如:Android studio编译后会在app\build\intermediates\javac\release\compileReleaseJavaWithJavac\classes
目录下生产编译后的class文件

  • 即时编译(JIT编译)

通过Java虚拟机(JVM)内置的即时编译器(Just In Time Compiler,JIT编译器);在运行时把Class文件字节码编译成本地机器码的过程;

  • 静态提前编译(AOT编译)

程序运行前,直接把Java源码文件(.java)编译成本地机器码的过程

jvm运行原理

在这里插入图片描述

解析器与JIT编译理解

简单来说,java代码—》虚拟机—》cpu执行,就是将java代码最终转化为cpu执行的机器码,解析器就是这个功能,但是一边解析一边执行,这样效率太低了,所以增加了即时编译(JIT编译),就是运行时编译成机器码,它与解析器的区别就是,将运行频率很高的字节码直接编译为机器指令执行以提高性能,这样不用每执行一次就解析一次

静态提前编译(AOT编译)的优缺点

AOT是提前把整个程序编译成机器码,可以想象,执行性能是高的,但有两个明显的确定:1.占空间,每个应用程序占多一份机器码指令的空间 2.提前编译需要一定的时间,用户感知这个等待过程在低性能cpu下感知很明显

Android系统编译方式进化过程
  • 4.2及之前的版本

解释执行+JIT编译:效率很低,用户感知应用使用不流畅

  • 5.0-6.0版本

ART(Android runtime)代替,采用AOT编译方式:用户感知,流畅度明显提升,但是应用安装时间很慢,需要每次安装时完成编译,并且占用大量磁盘空间

  • 7.0版本

解析器+JIT+AOT的方式:提升运行时性能,节省存储空间,加快应用更新和系统更新速度

  • 8.0版本

解析器+JIT+AOT,改进了解释器,解释模式执行效率大幅提升:提示的流畅度

  • 9.0版本

解析器+JIT+AOT,提供了预先放置热点代码的方式,应用在安装的时候就能知道常用代码会被提前编译:ART 提前编译器通过将应用软件包中的 DEX 文件转换为更紧凑的表示形式,进一步优化了压缩的 Dalvik Executable 格式 (DEX) 文件。 此项变更可让您的应用启动更快并消耗更少的磁盘空间和内存

虚拟机字节码执行引擎

学习1

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值