虚拟机
2019-06-20 17:37:32 0 举报
AI智能生成
虚拟机
作者其他创作
大纲/内容
垃圾回收
概念
JVM来说,垃圾指的是在堆中死亡的对象所占据的内存空间
怎么知道对象死没死
引用计数法
概念
它的做法是为每个对象添加一个引用计数器,用来统计指向该对象的引用个数。一旦某个对象的引用计数器为0,就
判断为死亡
判断为死亡
缺点
额外的空间来存储计数器,以及繁琐的更新计数器以外;引用计数法还有一个重大的漏洞:无法处理循环引用
可达性分析
概念
将一系列被称为GC Roots的变量作为初始的存活对象合集,然后从该合集出发,所有能够被该集合引用到的对象,
并将其加入到该集合中,而不能被该合集所引用到的对象,并可对其宣告死亡。
并将其加入到该集合中,而不能被该合集所引用到的对象,并可对其宣告死亡。
缺点
在多线程环境下,其他线程可能会更新已经访问过的对象中的引用,而我们的可达性分析线程却没有同步到最新的内容。
那么就会造成误报或者漏报。对于JVM来说误报顶多损失了部分垃圾回收的机会。漏报则比较麻烦,因为垃圾回收器可能
回收了仍被引用的对象…
那么就会造成误报或者漏报。对于JVM来说误报顶多损失了部分垃圾回收的机会。漏报则比较麻烦,因为垃圾回收器可能
回收了仍被引用的对象…
解决方案
Stop-the-world以及安全点
垃圾回收的三种方式
清除(sweep)
概念
把死亡对象所占据的内存标记为空闲内存,并记录在一个空闲列表(free list)之中。当需要新建对象时,
内存管理模块便会从该空闲列表中寻找空闲内存,并划分给新建的对象。
内存管理模块便会从该空闲列表中寻找空闲内存,并划分给新建的对象。
缺点
1、造成内存碎片。由于 Java 虚拟机的堆中对象必须是连续分布的,因此可能出现总空闲内存足够,但是无法分配的极端情况。
比如:总空间100M,此时我们需要申请100M的数组。但是由于内存不连续,因此我们就会申请失败。2、分配效率较低。
如果是一块连续的内存空间,那么我们可以通过指针加法(pointer bumping)来做分配。而对于空闲列表,Java 虚拟机则
需要逐个访问列表中的项,来查找能够放入新建对象的空闲内存。
比如:总空间100M,此时我们需要申请100M的数组。但是由于内存不连续,因此我们就会申请失败。2、分配效率较低。
如果是一块连续的内存空间,那么我们可以通过指针加法(pointer bumping)来做分配。而对于空闲列表,Java 虚拟机则
需要逐个访问列表中的项,来查找能够放入新建对象的空闲内存。
压缩(compact)
概念
把存活的对象聚集到内存区域的起始位置,从而留下一段连续的内存空间。
缺点
压缩算法的性能开销
优点
能够解决内存碎片化的问题
复制(copy)
概念
把内存区域分为两等分,分别用两个指针 from 和 to 来维护,并且只是用 from 指针指向的内存区域来分配内存。当发生
垃圾回收时,便把存活的对象复制到 to 指针指向的内存区域中,并且交换 from 指针和 to 指针的内容。
垃圾回收时,便把存活的对象复制到 to 指针指向的内存区域中,并且交换 from 指针和 to 指针的内容。
现代设计方案
一般是把Java堆分作新生代和老年代,根据各个年代的特点采用最适当的收集算法
运行流程(概要)
java程序编译成字节码,经jvm解释成机器码,最终在操作系统上执行
类加载过程
1.加载
类加载器classLoader加载把java字节码加载到内存
2.连接
2.1验证
确保类加载的正确性。一般情况由javac编译的class文件是不会有问题的,但是可能有人的class文件是自己通过其他方式编译出来的,这就很有可能不符合jvm的编 译规则,这一步就是要过滤掉这部分不合法文件
2.2准备
为类的静态变量(static修饰的成员变量)分配内存,将其初始化为默认值 。我们都知道静态变量是可以不用我们手动赋值的,它自然会有一个初始值 比如int 类型的初始值就是0 ;boolean类型初始值为false,引用类型的初始值为null 。 这里注意,只是为静态变量分配内存,此时是没有对象实例的
2.3解析
把类中的符号引用转化为直接引用。解释一下符号引用和直接引用。比如在方法A中使用方法B,A(){B();},这里的B()就是符号引用,初学java时我们都是知道这是java的引用,以为B指向B方法的内存地址,但是这是不完整的,这里的B只是一个符号引用,它对于方法的调用没有太多的实际意义,可以这么认为,他就是给程序员看的一个标志,让程序员知道,这个方法可以这么调用,但是B方法实际调用时是通过一个指针指向B方法的内存地址,这个指针才是真正负责方法调用,他就是直接引用。
3.初始化
为类的静态变量赋予正确的初始值,上述的准备阶段为静态变量赋予的是虚拟机默认的初始值,此处赋予的才是程序编写者为变量分配的真正的初始值
运行时数据区
方法区
jdk1.3~1.6 方法区用于存储虚拟机加载的1.类信息,2.常量,3.静态变量,4.编译器编译后的代码
jdk1.7 方法区存储1.类信息,2.编译后的代码
jdk1.8 方法区不存在,类信息,编译后的代码移动到元空间(MetaSpace),元空间直接占用本地内存
堆区
主要用于存放对象和数组,它是jvm管理的内存中最大的一块区域
虚拟机栈
每个线程分配一个虚拟机栈,每个虚拟机栈中都有若干个栈帧(一个方法就是一个栈帧),每个栈帧存储了局部变量表,
操作数栈,动态链接,返回地址等.当线程执行方法时,就代表这个方法对应的栈帧已经进入虚拟机栈并且处于栈顶的位置
,每一个java方法从被调用到执行结束,就对应了一个栈帧从入栈到出栈的过程.
操作数栈,动态链接,返回地址等.当线程执行方法时,就代表这个方法对应的栈帧已经进入虚拟机栈并且处于栈顶的位置
,每一个java方法从被调用到执行结束,就对应了一个栈帧从入栈到出栈的过程.
本地方法栈
虚拟器栈执行的是java方法,本地方法栈执行的是本地方法,其他基本一致
程序计数器
可以看作当前线程执行字节码的行号指示器,每个线程都有一个独立的程序计数器,此外,他是jvm规定唯一不会发生内存溢出的区域
堆内存溢出
堆内存溢出
栈内存溢出
方法区溢出
0 条评论
下一页