虚拟机

###前言

本篇文章将会介绍Java虚拟机(JVM)的结构等基础知识、Dalvik(DVM)/ART虚拟机与JVM的比较。

###JVM结构

####JVM的整体结构

将以JVM为代表讲解,因为DVM以及ART都是基于JVM进行改造的。

JVM结构如下图:

相关描述:

  • 类加载器子系统的核心--ClassLoader负责将class字节码加载到虚拟机内存中。
  • 虚拟机的内存按照逻辑划分为4大部分
  • 垃圾收集器GC负责回收对象
  • 本地方法库提供了native方法,比如C/C++
  • 其余的都是跟CPU等硬件打交道的,我们了解一下即可

下面从代码的编译到执行过程对这个结构进行详细分析。

####一、编译流程

JVM字节码即class文件,通过javac命令生成class文件的详细过程如下:

其中最核心的就是编译器对词法、语法的分析,如果能够写一个编译器,那么就可以创造一门语言了。

####二、类加载器

接下来看一下JVM的类加载器(Android的类加载器跟原生的类加载器不一样,但是都大同小异):

如上图所示,不同的类加载器加载不同的字节码文件,其中:

  • rt.jar(Runtime)就是Java运行时的核心jar包
  • ext也是Java的核心jar包
  • Custom ClassLoader是自定义的类加载器,可以加载用户指定目录的jar文件。这是动态加载的理论依据。

类加载流程如下:

具体过程如下:

  1. Loading:从class文件中获取类的信息,并且载入JVM内存
  2. Verifying:检查读入的结构是否符合JVM规范的描述
  3. Preparing:分配一个结构用来存储类的信息
  4. Resolving:把类的常量池中的所有符号引用改变为直接引用
  5. Initialzing:执行初始化程序,把静态变量初始化为指定的值,执行static块等等

####三、JVM内存管理

回顾一下这个图,JVM的内存按照逻辑分为4大部分:

  1. Java栈:主要作用是存放Java方法执行的时候所有的数据,由栈帧组成,一个栈帧代表一个方法的执行。每个方法从调用到执行完成就对应一个栈帧在虚拟机中的入栈到出栈。栈帧的信息包括局部变量表、栈操作数、动态链接、方法出口。
  2. 本地方法栈:专门为native方法服务的,例如C、C++方法。
  3. 方法区:存储被虚拟机加载的类信息、常亮、静态变量、即时编译器编译后等数据。
  4. 堆区:所有通过new创建的对象的内存都在堆中分配。堆内存分为新生代和老生代,新生代存储的是刚创建的对象。当内存不足的时候,虚拟机就会通过一系列算法把新生代的对象移动到老生代。

######Tips1:当方法栈的深度大于JVM最大深度的时候,就会栈溢出。例如写一个没有退出的递归就会导致栈溢出(StackOverflow)。 ######Tips2:当新生代、老生代都满了的话,就会导致内存溢出(OOM)。 ######Tips3:新、老生代的动态调整是服务端开发需要掌握的一门技术,比如做即时通信服务端,Message临时对象比较多,那么适当增加新生代,便于内存分配,加快对象的创建。做大型服务类程序,不需要频繁创建,就可以扩大老生代,达到对象常驻效果,保证服务稳定性。

####四、垃圾回收

下面来讲解一下虚拟机的垃圾回收(GC)的知识。主要包括垃圾的确定与收集、垃圾的回收、垃圾回收的时机。垃圾回收主要针对堆内存。

垃圾收集算法有:

  • 引用计数法:被引用1次,计数器加1,没有被引用的时候,则回收。但是引用计数法无法解决对象之前相互引用的问题,因此已经废弃。
  • 可达性算法(根搜索算法):通过GC ROOT对象开始搜索,不可达的对象则回收。这时候可以提到引用的类型,主要用得最多就是强引用和弱引用。当存在强引用的时候,内存不足宁愿抛出OOM也不会回收。但是是弱引用的话,就有可能会被回收,这样就防止了内存泄漏。

垃圾回收算法主要有:

  • 标记-清除算法:搜索,发现没有引用的对象,直接回收,但是会导致内存碎片过多。
  • 复制算法:搜索,扫描没有引用的对象。开辟新的内存空间,将存活的对象复制到新的内存,旧的内存直接清除。由于需要多次交换内存空间,因此在对象数量比较少的时候效率比较高。
  • 标记-整理算法:在标记-清除算法的基础上,清除掉不存活的对象之后,把后面的存活对象搬移过来,似的内存连续,解决了内存碎片的问题。

三种算法的过程图解如下所示:

######Tips:三种算法是混合使用的,不同情况,例如对象数量不同,采用不用的算法,已达到最大的效率。

触发垃圾回收主要有:

  • 当年轻代或者老年代满了,Java虚拟机无法为新的对象分配内存空间了
  • 手动调用System.gc()方法,通常这样会触发一次的Full GC以及至少一次的Minor GC,但是并不会导致GC马上执行,反而会增加了虚拟机的负担,因此不推荐直接使用
  • 程序运行的时候有一条低优先级的GC线程,它是一条守护线程,当这条线程处于运行状态的时候,自然就触发了一次GC了。

###Dalvik VM(DVM)

为了更加适合移动端,Android基于JVM创造了DVM:

  • JVM执行的是class文件,DVM执行的是dex文件
  • 类加载系统与JVM区别比较大
  • 可以同时存在多个DVM
  • JVM是基于栈的,DVM是基于寄存器的,寻址速度更加快

###ART

ART虚拟机是DVM的进化版本:

  • 运行的时候,DVM采用JIT来将字节码转换成机器码,效率比较低
  • ART采用了AOT预编译技术,APP安装的时候就完成了字节码转换为机器码,执行速度更加快,效率高
  • 但是ART会占用更多的安装时间以及存储空间,这是典型的以空间换区时间的策略

如果觉得我的文字对你有所帮助的话,欢迎关注我的公众号,我致力于各种技术的分享(尤其是各种视频教程),畅聊各种工程师成长经验,并包括各种办公室外的思考:书籍推荐、高效学习方法、写作、投资理财、创业、商业、运营、产品、时间管理、改变习惯等:

我的群欢迎大家进来探讨各种技术与非技术的话题,有兴趣的朋友们加我私人微信huannan88,我拉你进群交(♂)流(♀)

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值