在网站上有中英切换怎么做,好点的开发网站的公司,微信公众号怎么做链接网站,wordpress 自定义栏目 删除目录
1.JVM内存划分
2.JVM类加载过程
3.JVM垃圾回收机制GC
3.1.判断谁是垃圾
3.2.如何释放对应的内存 1.JVM内存划分
在一个Java程序运行起来之后#xff0c;jvm就会从操作系统中申请一块内存#xff0c;然后就会将该内存划分成多个部分#xff0c;用于不同的用途。
…目录
1.JVM内存划分
2.JVM类加载过程
3.JVM垃圾回收机制GC
3.1.判断谁是垃圾
3.2.如何释放对应的内存 1.JVM内存划分
在一个Java程序运行起来之后jvm就会从操作系统中申请一块内存然后就会将该内存划分成多个部分用于不同的用途。
1主要划分成五个部分
堆、栈、元数据区(方法区)、常数计数器(很少涉及)
图示 像堆区中的各个部分划分在后面的垃圾回收机制再拿出来鞭策一遍在这里就不先做任何的赘述。
2每块内存区的功能
堆区 整个内存区域中最大的区域用于存放java代码中new出来的对象、和成员变量。 栈 一般java是使用jvm虚拟机栈这里保存了方法的调用关系、局部变量等。也就是每个方法怎么被调用被谁调用等 元数据区 元数据区以前也成为方法区用来存放类对象、类属性(静态成员)、常量 程序计数器 属于内存中最小的区域用来保存要执行的下一条指令的地址 3实战划分内存
看下嘛的一段代码查看下面的变量和对象分别处于哪一块内存中
class Demo2 {}
class Demo1 {public int a;Demo2 b new Demo2();public String c love;public static int d;}public class Test {public static void main(String[] args) {Demo1 e new Demo1();}
}
对于变量a,b,c,d,e 其中a,b,c都属于成员变量存在于堆上而e属于局部遍历存在栈上而d属于静态成员变量属于类属性存在于元数据区。它们只是属于变量里面存有一个值会指向另一块内存空间。 各对象内存分布和指向关系 以上就是对应的各种关系。 对于上述的四个区域堆和元数据区在整个进程中只有一份而栈和程序计数器是每个线程都有一份。 2.JVM类加载过程
一个.java文件变成.class文件的过程也是从硬盘加载到内存中得到类对象的过程。
1类加载的五个环节
加载、验证、准备、解析、初始化
2每个环节对应的作用
1.加载
在硬盘上找到对应的.class文件并且读取.class文件内容
2.验证
检查.class文件中的内容是否符合要求(如文件格式等)
3.准备
给类对象分配内存空间
4.解析
对字符串常量初始化把刚才.class文件中的常量内容取出来放到元数据区
5.初始化
针对类对象进行初始化给静态成员初始化也就是执行静态代码块
3“双亲委派模型”
在第一步的加载环节目的是打开.class文件前提就是需要通过“全限定类名”找到文件才能进行打开所以“双亲委派模型”就是寻找.class文件的一种机制。
在这个环节涉及到一个概念类加载器 在JVM中也会包含一些类负责完成后续的类加载工作。其中JVM内置了三个类加载器负责加载不同的类 分别是三个类BootstrapClassLoader、ExtentionClassLoader、ApplicationClassLoader
1三个类加载器作用 所以成为双亲委派模型但是跟准确的说法为父亲委派模型或者单亲委派模型
2加载过程
那么双亲委派模型是如何找类的呢我们举一个例子假设我们自己写了一个java程序会给定一个全限定类名 3双亲委派模型应对的场景
如果自己的代码中写的类的名字和标准库/扩展库冲突了JVM会确保加载的类是标准库的类(不会加载自己写的类)如果标准库中的类无法加载那么Java进行就没有办法正常工作了。
这样还有一个好处就是可以确保自己写的类肯定可以被加载到。 3.JVM垃圾回收机制GC
对于Java回收垃圾采取的是自动回收策略策略也称为GC。
对于GC来说回收的其实是堆上的内存。而对于堆保存的主要是对象换句话说也就是主要回收对象那怎么回收对象呢主要有两个步骤判断谁是垃圾和如何释放其对应的内存。 3.1.判断谁是垃圾
在判断谁是垃圾这一步Java是采取很保守的做法也就是可以保证只会释放后续不会再使用的对象后续仍会使用到的对象是不会进行回收的所以才用的策略是判断某个对象是否存在引用指向如果没有引用指向就可以判断为垃圾反之不行。
判断谁是垃圾GC有两种策略引用技术和可达性分析而JVM采取的策略只有可达性分析引用计数则不是。 1引用计数
1策略每创建一个对象就在对象前面多开辟一块空间用来计数使用有一个引用指向该对象计数变量就1如果计数器为0则需要回收该对象。
2代码举例
class TT {}
public class GC {public static void main(String[] args) {TT a new TT();TT b a;}
}
对于实例化的TT对象当前有两个对象指向它所以计数值为2分别是引用a和b如果anull或者bnull则计数值-1两个都置为null则计数为0.
对于引用计数存在两个问题
问题一会消耗额外的内存空间 如果对象本身的内存比较大相比来说计数的空间就很小但是如果对象内存空间很小那么计数空间就会显得很大就会浪费很大的空间 问题二存在“循环引用”问题
这类问题就会让外部代码无法对 对象进行释放
代码举例
class T {T t;
}
public class GC {public static void main(String[] args) {T a new T();T b new T();a.t b;b.t a;}
}
这是一段有问题的代码
图示分析 存在的问题当将引用a和b置为null时按理来说这两个对象是要被回收了但是这里却不会因为计数不为0不能回收。所以这就是引用计数最大的一个问题。 2可达性分析
JVM采取的是可达性分析既解决了空间问题也解决了循环引用问题。
1定义JVM会把对象之间的引用关系定义成树形结构JVM可以不停的从根节点开始遍历可以访问到的对象成为“可达”剩下的就是“不可达”。
标记为不可达的对象也就被标记成垃圾。
2举例 例如这样的结构如果将cnull那么c后面的两个对象f和g就变成不可达就要成为垃圾。
访问这棵树的所有节点都是要通过根节点a开始。
3确定根节点
对于根节点可能的情况有栈上的引用局部变量、常量池中引用的对象、方法区中的静态成员。 3.2.如何释放对应的内存
当已经标记好垃圾之后该怎么回收呢下面介绍。一共四种前面三种是铺垫最后一种才是JVM真正使用的
1标记-清除
策略直接把标记为垃圾对象的对应的内存回收已经回收的内存其他对象可以使用
缺点这样做很存在内存碎片问题后续就很难申请到一大块连续的空间
2复制算法
策略要删除空间1中abcd中的ac就直接将bd复制到另一块空间中
缺点严重浪费空间。比如有8G内存就只能使用4G
3标记-整理
策略把不需要释放的内存空间覆盖到需要释放的空间上。可以解决内存碎片问题
缺点时间开销很大
4分代回收
策略根据不同的场景采取不同的回收策略这里的回收策略也就是上述介绍到的。
如何根据场景呢就是要根据时间来哪个时间呢就是对象的年龄。
因为GC主要回收的地方就是堆区所以堆区上会有对应的分区不同的区代表对象的年龄 Eden:称为伊甸区新创建的对象都处于这里。 这里的对象生命周期都很短一般经过一轮GC就会成为垃圾。从伊甸区到达生存区采取的是复制算法 s0:称为生存区一般经过一轮还未被回收就会到达下一个阶段 从生存区到达幸存区也是通过复制算法 s1:称为幸存区 这里也称为生存区到达这里也需要通过复制算法。 Old区:到达这里的对象都会成为老年代的对象GC的扫描频率会大幅度降低。 对于老年代中的垃圾就会通过标记-整理的方法进行回收 总结一下伊甸区、生存区、幸存区都是通过复制算法回收垃圾很搬运到下一个区对于老年区则是通过标记-整理回收垃圾。