Java之所以能够如此流行并且经久不衰,其
一次编写,到处运行
的特性起到很重要的作用,引出今天的第一个问题。
因为Java程序编译之后会生成.class
文件,这种文件是不能够被硬件系统直接运行的代码,而是一种中间码
——字节码。如果硬件系统想要运行这种代码,就需要JVM的帮助。
JVM(Java Virtual Machine),中文一般叫做Java虚拟机,在不同的硬件平台上安装有不同版本的Java虚拟机,在Java程序编译后,由JVM将字节码翻译
成对应平台能够执行的代码。因此对于Java编程者来说,根本不需要考虑硬件平台是什么,可以更加专注在程序本身的逻辑。
Java的跨平台就是通过在不同的平台安装相应的JVM来实现的
JVM是不能够单独安装的,他是嵌入在JDK或JRE中的。
名词解释
JDK:Java Development Kit(Java开发工具包) JDK作为开发工具,一般都是包含JRE的。
JRE:Java Runtime Environment(Java运行时的环境) JRE本质上是通过JVM虚拟机来运行
JVM:Java Vitual Machine(Java虚拟机)Java的跨平台就是通过在不同的平台安装相应的JVM来实现的。
Java虚拟机有自己完善的硬件架构,如处理器、堆栈等,还具有相应的指令系统。
1.方法区是静态分配的,编译器将变量绑定在某个存储位置上,而且这些绑定是不会在运行时改变。常量池
,源代码中的命名常量
、String常量
和static变量
保存在方法区。
2.Java stack
是一个逻辑概念,特点是后进先出。一个栈的空间可能是连续的,也可能是不连续的。最典型的stack应用是方法的呃调用,Java虚拟机每调用一次方法就创建一个方法帧(frame)
,退出该方法则对应的方法帧被弹出(pop)
。栈中存储的数据也是运行时确定的。所谓的虚拟机栈就是常说的栈。
本地方法栈和虚拟机栈所发挥的作用非常相似,区别是:虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。在HotSpot
虚拟机中的Java虚拟机栈合二为一
3.Java堆分配(heep allocation)
意味着以随意的顺序,在运行时进行存储空间分配和收回的内存管理模型。堆中存储的数据常常是大小、数量和生命周期在编译时无法确定的。Java对象的内存总是在heap中分配。我们每天都在写代码,每天都在使用JVM的内存。
4.程序计数器
:字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程切换回来的时候能够知道该线程上次运行到哪个位置。
5.JDK1.8的时候,方法区(HotSpot的永久代)被彻底移除了,(JDK1.7就已经开始了),取而代之的是元空间,元空间使用的是直接内存
1.基础数据类型直接在栈空间分配
2.方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间释放
3.引用数据类型,需要用new
来创建,既在栈空间分配一个地址空间,又在堆空间分配对象的类变量
4.方法的引用参数,在栈空间分配一个地址空间,并指向堆空间的对象区,当方法调用完后从栈空间回收
5.局部变量new
出来时,在栈空间和堆空间中分配空间,当局部变量生命周期结束之后,栈空间会立刻被回收,堆空间区域等待GC回收
6.方法调用时传入的实际参数,先在栈空间分配,在方法调用完成后从栈空间释放
7.字符串常量在DATA区域分配,this在对空间分配
8.数组既在栈空间分配数组名称,又在堆空间分配数组实际的大小
理论上来讲,Sun公司只定义了垃圾回收机制规则而不局限于其实现算法,因此不同厂商生产的虚拟机采用的算法也不尽相同。
GC(Garbage Collector)
在回收对象前首先必须发现那些无用的对象,如何去发现定位这些无用的对象?常用的搜索算法如下:
引用计数器算法是给每个对象设置一个计数器,当有地方引用这个对象的时候,计数器+1,当引用失效的时候,计数器-1,当计数器为0的时候,JVM就认为对象不再被使用,是垃圾了。引用计数器实现简单,效率高但是不能解决循环引用问题(A对象 引用 B对象,B对象 又引用 A对象,但是A,B对象已不被任何其他对象引用),同时每次计数器的增加和减少都带来了很多额外的开销,所以在JDK1.1之后,这个算法已经不在使用了
根搜索算法是通过一些GC Roots对象作为起点,从这些节点开始往下搜索,搜索通过的路径成为引用链Reference Chain,当一个对象没有被GC Roots的引用链连接的时候,说明这个对象是不可用的
GC Roots 对象包括
a).虚拟机栈(栈帧中的本地变量表)中的引用的对象
b).方法区域中的类静态属性引用的对象
c).方法区域中常量引用的对象
d).本地方法栈中JNI(Native方法)的引用的对象
通过上面的算法搜索到无用对象之后,就是回收过程,回收算法如下:
标记-清除算法包括两个阶段:标记和清除。在标记阶段,确定所有要回收的对象,并做标记。清除阶段紧随标记阶段,将标记阶段确定不可用的对象清除。标记-清除算法是基础的收集算法,标记和清除阶段的效率不高,而且清除后会产生大量的不连续空间,这样当程序需要分配大内存对象时,可能无法找到足够的连续空间
复制算法是把内存分成大小相等的两块,每次使用其中一块,当垃圾回收的时候,把存活的对象复制另一块上,然后把这块内存整个清理掉。复制算法实现简单,运行效率高,但是由于每次只能使用其中的一半,造成内存的利用率不高。现在的JVM用复制方法收集新生代,由于新生代中大部分对象(98%)都是朝生夕死的,所以两块内存的比例不是1:1(大概8:1)
标记-整理算法和标记-清除算法一样,但是标记-整理算法不是把存活对象复制到到另一块内存,而是把存货对象往内存的一端移动,然后直接回收边界以外的内存。标记-整理算法提供了内存的利用率,并且它适合在收集对象存活时间较长的老年代
分代收集是根据对象的存活时间把内存分为新生代和老生代,根据各个对象的存活特点,每个带采用不同的垃圾回收算法。新生代采用复制算法,老年代采用标记-整理算法。垃圾算法的实现涉及大量的程序细节,而且不同的虚拟机平台实现的方法也各不相同。
JVM是Java的虚拟机,其大致可以包括
类加载器:用于加载类
垃圾回收器:回收无用的对象
方法区:也称为永久代
、非堆
,它用于存储虚拟机加载的类信息、常量、静态变量、是各个线程共享的内存区域
Java堆:java虚拟机所管理的内存中最大的一个块内存区域,也是被各个线程共享的内存区域,在JVM启动时创建。该内存区域存放了对象实例及数组(所有new出来的对象)。由于现在收集器都是采用分代收集算法,堆被划分为新生代和老生代。新生代主要存储新创建的对象和尚未进入老年代的对象。老年代存储经过多次新生代GC(Minor GC)仍然存活的对象。
Java栈:描述的是Java方法执行的内存模型:每个方法被执行的时候都会创建一个栈帧,用于存储局部变量表(包括参数)、操作站、方法出口等信息。每个方法被调用到执行完的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。生命周期与线程相同,是线程私有的。
本地方法栈:与虚拟机栈基本类似,区别在于虚拟机栈为虚拟机执行的Java方法服务,而本地方法栈则是为Native方法服务
执行引擎
本地方法接口
指令寄存器
原文:https://www.cnblogs.com/Breety/p/15154542.html