如何利用网站开发国外客户,东莞seo建站优化公司,唐山网站建设优化,网站关键词做排名不分目录 一.简介
二.JVM中的内存划分
JVM的内存划分图:
堆区:编辑
栈区:编辑
程序计数器#xff1a;编辑
元数据区#xff1a;编辑
经典笔试题#xff1a;
三,JVM的类加载机制
1.加载:
2.验证:
3.准备:
4.解析:
5.初始化:
双亲委派模型
概念: JVM的类加…目录 一.简介
二.JVM中的内存划分
JVM的内存划分图:
堆区:编辑
栈区:编辑
程序计数器编辑
元数据区编辑
经典笔试题
三,JVM的类加载机制
1.加载:
2.验证:
3.准备:
4.解析:
5.初始化:
双亲委派模型
概念: JVM的类加载器 默认有三种
双亲委派模型的工作流程
四.JVM的垃圾回收机制(GC) 垃圾回收步骤
1.识别出垃圾
1引用计数
2可达性分析
2.把标记为垃圾的对象的内存空间进行释放
1标记-清除
2复制算法
3标记整理
分带回收 一.简介
JVM : java Virtual Machine 的简称意为Java虚拟机。
虚拟机是指通过软件模拟的具有完整硬件功能的、运⾏在⼀个完全隔离的环境中的完整计算机系统。常⻅的虚拟机JVM、VMwave、Virtual Box。
JVM 和其他两个虚拟机的区别
1. VMwave与VirtualBox是通过软件模拟物理CPU的指令集物理系统中会有很多的寄存器
2. JVM则是通过软件模拟Java字节码的指令集JVM中只是主要保留了PC寄存器其他的寄存器都进⾏了裁剪。
JVM 是⼀台被定制过的现实当中不存在的计算机。 java的执行流程是先通过javac 将.java文件转为.class字节码文件文件之后在某个平台执行然后 通过JVM 将.class文件转换为CPU能识别的机器指令。
因此编写一个java程序只需要发布.class文件就行了。JVM拿到.class文件,就知道该如何转换了.
二.JVM中的内存划分
JVM也相当于一个进程,在启动一个java程序后,需要个JVM分配资源空间.
JVM从系统中申请的内存,会根据java程序中不同的使用途径,为其分配空间.这就是内存划分.
JVM会将申请到的空间划分成几个区域,每个区域有不同的功能,
JVM的内存划分图: 堆区:
存放的是代码中new出来的对象,对象中的非静态成员也在堆区.
栈区:
包含了一些方法调用关系和局部变量.
由本地方法栈和虚拟机栈组成,本地方法栈是JVM内部,是由C写的虚拟机栈保存了一些java的方法调用和局部变量。
平时所说的栈区指的是虚拟机栈
程序计数器
这个区域比较小专门用来保存下一条要执行的java指令的地址。
元数据区
包含了一些辅助性质的描述性质的属性。元数据区也叫做方法区。
元数据是计算机中的一个常见术语Meta data。
对于硬盘来说,不仅要存储文件的数据本体还要存储一些辅助信息像文件的大小文件的位置文件的使用权限文件的拥有者....这些都称为“元数据”。
一个程序中有哪些类有哪些方法每个方法中有哪些指令....这些信息都会保存在JVM的元数据区. 对于堆区和元数据区,整个进程中只有一份;而对于栈区和程序计数区,在内存中是有很多份的.
经典笔试题
class Test {private int n;private static int m;
}
public static void main(String args[]){Test t new Test();
}
问: n,m,t 都在哪块JVM的哪个内存区域中?
n属于局部变量,在作用域中生效,出作用域就销毁了,存在栈区.
m属于静态变量存在元数据区。
t是new出来了一个Test对象t中保存的是Test的地址,属于局部变量,保存在栈区;而Test对象则保存在堆区. 区分变量在内存的哪个区域上,最重要的就是确定该变量的形态,是 局部变量/成员变量/静态变量....
三,JVM的类加载机制
类加载指的是JVM把.class文件从硬盘读取到内存,进行一系列的校验解析的过程.转换成类对象的过程.
类加载过程大致分为五步:
1.加载:
把.class文件找到并打开,读取到文件中的内容.
2.验证:
需要确定当前读到的文件是合法的.class文件(字节码文件).否则若读到错误的文件,后面的工作就白费了.
具体的验证依据是在java的虚拟机规范中,有明确的格式说明: 左面这一列是类型,右面这一列是名字.
:
也叫做:magic number 魔幻数字,用来标识二进制文件中的格式的类型.
:
这两个都是版本号,u4 是主版本,u2 是次版本.属于JVM内部的版本,JVM会验证.class文件的版本号是否符合要求.
一般来说 高版本的JVM可以运行低版本的.class文件,反之不行.
3.准备:
为类对象申请内存空间.此时申请到的内存空间都为默认值 为全0的.
4.解析:
主要是针对类中的字符串常量进行处理.
将常量池中的 符号引用 替换为 直接引用 的过程,也就是初始化常量的过程.
我们知道,在.class文件中,是不存在地址的,而对于创建的字符串常量,变量中保存的是常量的地址,这又是怎样记录的呢?
class Test{private String shello;
}
这个hello在.class文件中,是否会保存呢?
当然是要保存的,只不过s中保存的是一个字符串常亮的偏移量. 在文件中,不存在地址这样的概念,地址是内存的地址,而文件是在硬盘中的.
为了保存字符串常来那个,可以存储一个偏移量的概念, 这里的偏移量就认为是符号引用.
之后,把.class文件加载到内存中,就有地址了,s中的值就能根据偏移量来转换为真正地址了,也就是直接引用.
5.初始化:
针对类对象,完成后续的初始化操作.
执行静态代码块,构造方法,还可能触发父类加载.....
双亲委派模型
在类加载过程的第一步:加载环节中使用 双亲委派模型 描述如何查找.class文件的策略.
JVM在进行类加载的时候,有一个专门的模块,称为类加载器.(ClassLoader)
概念: 双亲委派模型: 如果一个类加载器收到一个类加载的请求,他首先不会自己加载该类,而是将这个类委派给父类加载器,让父类加载器去完成对类的加载.每层次的类加载器都是这样委派,最终所有的加载请求都会到达 最顶层的类加载器,直到当父类加载器反馈自己无法完成这个类加载请求时,子类加载器就会尝试自己完成加载. JVM的类加载器 默认有三种
BootstrapClassLoader: 负责查找标准库目录.
ExtensionClassLoader: 负责查找扩展库目录.
ApplicationClassLoader: 负责查找当前项目的代码目录,以及第三方库. 这三个类加载器存在父子类(二叉树关系)关系.
ApplicationClassLoader的父类是ExtensionClassLoader
ExtensionClassLoader的父类是BootstrapClassLoaderBootstrapClassLoader属于顶层父类。
双亲委派模型的工作流程
1.类加载任务先从ApplicationClassLoader为入口开始工作
2.ApplicationClassLoader自己不会立即搜索自己负责的目录会将搜索的任务向上传递给父类
3.代码进入ExtensionClassLoader的范畴,同样,ExtensionClassLoader 也不是立即搜索自己负责的目录,继续将搜索的任务向父类传递;
4.代码进入BootstrapClassLoader的范畴,由于BootstrapClassLoader是顶级父类了,就会真正进行负责搜索目录(标准库目录),尝试在标准库目录中找到符合要求的.Class文件;
5.若是找到了,就会进入打开文件,读文件流程了,此时类加载步骤就结束了若是没有找到就会返回到子类的类加载器中继续尝试加载。
6.若是在ExtensionClassLoader类加载器中找到符合要求的.Class文件,此时类加载步骤就结束了;若还未找到,就会返回给子类加载器ApplicationClassLoader继续尝试加载.
7.若在ApplicationClassLoader类加载器中搜索到了,此时类加载就结束了,就会进入后续流程若是没有找到就会继续向子类寻找由于ApplicationClassLoader是底层了,就表示类加载失败了. 这一系列的列加载机制,目的是为了保证这几个类加载器的优先级顺序.
这个类加载器是系统默认的类加载机制,也可以自己实现类加载机制,可以与默认机制不同.
四.JVM的垃圾回收机制(GC)
垃圾回收指的是让程序自动回收内存,JVM中的内存分为好几种,要回收的是堆区的内存
元数据区和程序计数区的内存不需要回收,栈区中存放的都是局部变量申请的内存在代码结束后,会自动销毁(属于栈区自己的特点,和垃圾回收没有关系)。 回收内存其实就是回收对象垃圾回收时将堆区上的若干个对象释放掉。
堆区内存根据垃圾回收又分为三类区间 垃圾回收步骤
1.识别出垃圾
要判定哪些对象是垃圾哪些对象不是垃圾。就是判断该对象是否还需要使用。
在java中使用对象一定是通过引用指向使用对象的方式使用若该对象没有引用指向则表示该对象不再被使用就可以进行垃圾回收了。
class Test{
....
}
void func(){
Test t new Test(); }
这个代码中,执行结束后,t属于局部变量存在于栈区会被直接释放掉Test对象在执行完后,由于没有对象指向了,也就属于垃圾了就会被垃圾回收。
对于一些更复杂的代码判定过程也就更加复杂。
Test t1 new Test();
Test t2 t1;
Test t3 t2;
Test t4t3;
....
很多引用都指向了同一个对象Test只有当所用的引用都结束了才能释放Test对象但每个引用的生命周期又不一样就很难判断了。
于是又设计一些方法来记录对象的引用
1引用计数 给每个对象再分配一个额外的空间保存当前对象引用个数当有一个引用指向了该对象引用计数就1一个引用结束后引用计数就-1. 此时的垃圾回收机制就是有一个专门的扫描线程取获取每个对象的引用计数的情况当引用计数为0时就表示该对象没有引用指向了不再使用了也就可以释放了。
class Test {....
}
void func() {
Test t1 new Test();
Test t2 t1;
}
这个代码的内存分配: 引用计数 存在的问题:
1耗费额外的空间
引用计数需要耗费一个额外的空间,若对象本身占用的内存就比较小,总的对象数目有很多,那么总的消耗空间就会非常多。
2可能出现“循环引用问题”
class Test{Test t;
}
Test t1 new Test();
Test t2 new Test();
t1.t t2;
t2.t t1;
t1 null;
t2 null;
当t1和t2还未被置为null的时候,此时的内存是这样的情况: 当t1和t2都被置为null后,t1,t2内存被释放,但Test对象中的t还未被释放: 此时,Test是的引用计数还都不0不能被GC回收但又无法使用就产生了循环引用问题这种情况下的引用计数就无法被正常使用了。
引用计数 这种思想 并未在java中使用在别的语言的垃圾回收机制中有使用到。
2可达性分析
JVM的垃圾回收机制 识别垃圾 采用的是这种思想
可达性分析本质上是采用“时间”换“空间”的方法。
相较于 引用计数可达性分析要消耗更多的时间去“遍历”不会存在上面 引用计数 中的问题。 可达性分析一个java代码中会定义很多变量从这些变量为起点向下“遍历”从这些变量中持有的引用类型的成员再向下遍历所有能被访问到的对象一定不是垃圾了而未被访问到的对象就是垃圾了要被就行回收。 JVM自身有扫描线程会不停地扫描代码看是否有对象无法被遍历到JVM本身是知道一共有多少个对象的。
class Node{char root;Node left;Node right;
}
Node BuildNode{
Node a new Node();
Node b new Node();
Node c new Node();
Node d new Node();
Node e new Node();
Node f new Node();
Node g new Node();
a.left b;
a.right c;
b.left d;
b.right e;
c.right f;
e.left g;
}
public static void main(String args[]){
Node root BuildNode();
}
代码中的树是这个样子, 在这个代码中,虽然只有一个root这样的引用,但实际上有7个对象都是可达的,
若代码中出现: c.rightnull;此时f就是不可达的,f就属于垃圾了.要进行回收.
若anull;那么整个二叉树都是不可达的了.都要进行垃圾回收.
2.把标记为垃圾的对象的内存空间进行释放
具体的释放方法有三种.
1标记-清除
把标记为垃圾的对象直接进行释放。最直接的方法
这种做法可能会产生大量的“内存碎片”会存在很多小的离散的可用空间。
可能导致后续申请内存空间失败申请内存空间都是一次申请一个连续的内存空间此时可能内存中总得空间是够当前要申请的内存空间的但内有连续的内存空间够分配就可能申请失败。
2复制算法
先把申请的内存分成两部分对象申请内存时都在一半的内存中创建进行释放时把不是垃圾的对象的内存复制到另一半内存中然后把带有垃圾的半个内存全部释放掉。 将不是垃圾的对象都复制到另半个内存中 再把左半部分的内存中的对象都释放掉 这个方法也存在一些问题
1、每次释放内存要释放一半的内存总的可用内存减少了很多。
2、若引用的对象很多对对象的复制也要消费很大的开销。
3标记整理
类似于顺序表中删除元素的方法。
遍历整个内存若有遍历到的对象标记为垃圾不用管后面遍历到不是垃圾的对象内存就覆盖垃圾的内存空间这样既不会存在“内存碎片”又不会一次释放很多的内存。 这个方法的缺点是搬运内存会有很大的开销。
上面的方法都有一定的缺点和问题因此JVM并没有直接使用上面的方法而是对上面的方法思想采用了一个·“综合性”方案“分带回收”。
分带回收
依据不同种类的对象采用不同的回收方式。
JVM引入了一个概念年龄。
JVM的扫描线程会不断的扫描内存若该对象是可达的年龄就1
JVM根据对象年龄的不同将内存分为两个区域新生代 和 老年代。
新生代中又划分了三个大小不等的区域其中一个大的区域叫伊甸区 和 两个小的等大的生存区幸存区。 回收过程
1.当创建出一个对象后该对象会先被创建到伊甸区伊甸区的对象大多都被第一轮GC扫描到了就会被回收掉
2.第一轮GC后少数存活的对象通过复制算法被送到其中一个生存区扫描还在继续生存区中被标记的对象就会被清除掉极少数的生存区的对象会再次通过复制算法从一个生存区复制到另一个生存区这样循环扫描复制每经过一轮GC的扫描年龄就会1.
3.当这个对象在生存区经过了若干轮扫描年龄已经很大了说明这个对象的生命周期可能很长就将这个对象拷贝到老年代老年代中的对象经过GC扫描的频率要比新生代低很多。 4.当扫描老年代中的对象也被标记为垃圾了也会进行释放。
这个分带回收就类似于找工作一样