)
这里写目录标题一、jvm内存区域划分1.程序计数器2.栈3.堆4.元数据区二、内存溢出1.栈溢出2.堆溢出三、JVM类加载机制1.类加载流程(1)加载(2)验证(3)准备(4)解析(5)初始化2.双亲委派模型垃圾回收GC1.找出谁是垃圾怎么判断有没有引用指向jvm就是JAVA虚拟机这个和我们软件虚拟机vmware不一样他只是针对JAVA程序针对字节码进行解释执行像python JavaScript都是需要虚拟机的编程语言为的是更好支持不同操作系统和CPUjdk是JAVA开发工具包jre是JAVA运行环境jvm是JAVA虚拟机它们一层包一层开发JAVA代码安装jdk就包含了jre不需要开发就只运行了一个JAVA程序只安装jre即可jvm很多底层东西和我们关系不大主要是高频面试题一、jvm内存区域划分每次运行JAVA程序的必然就创建了对应的JVMJAVA中使用的内存其实就是JVM内存。JVM内存是启动的时候从操作系统申请的应用程序后续需要使用的时候就可以从JVM内存进行分配不同区域1.程序计数器很小的区域只保留数字也就是下一条要执行JAVA字节码指令的地址在内存中通过软件维护2.栈面试的时候被问到那个啥是战和队我们要反问是哪个栈哪个堆是数据结构的还是JVM的还是操作系统分成虚拟机栈本地方法站虚拟机栈放局部变量给JAVA程序使用的栈维护了方法调用关系每调用一个方法就会有一个栈帧(frame里边放调用方法的实参方法内部的局部变量方法结束后又返回的上层方块的位置还有返回值完事一个删掉一个后进先出本地方法栈给C加加代码使用的JVM底层是C加加实现的放native本地方法的栈帧3.堆内存最大区域存放new的对象还有普通成员变量4.元数据区之前称之为方法区放类对象还有static修饰的成员用static修饰的属性就变成类属性而不是实例属性了实力属性是跟着对象走的类属性是跟着类走的给一段代码问其中的变量都是属于哪个内存区域中的二、内存溢出1.栈溢出栈里面包含了方法的调用关系栈帧太多了一般都是递归导致的2.堆溢出new的对象太多了无限循环往集合中添加元素以上内存区域针对程序计数器和栈存在多份的每个线程有一份自己的)堆和元数据区一个进程中只有一份了一个线程中new对象是可以直接被另一个线程使用的~~三、JVM类加载机制类加载就是.class文件读取到内存中构造出类对象过程触发这个创建实例使用静态方法或者静态属性使用子类影响到父类1.类加载流程(1)加载把.class文件找到打开文件并且读取文件的数据到内存中怎么找到JAVA代码中要使用到某个类需要import后面跟着全限定类名把代码中写的权限定类名找到对应的点class文件(2)验证根据读到的二进制内容检验是合法的格式(3)准备给要创建的类对象分配内存空间从元数据区安排一块内存保存这里的类对象JAVA默认把新申请的未初始化的内存全都设为0尝试获取static抽在你身边(4)解析针对字符串常量进行初始化需要把当前.class中的字符串常量也放到内存中此时字符串就有了起始地址顺便可以把这些地址取出来放到对应使用字符串的地方(5)初始化针对类对象进行初始化涉及到初始化的静态成员执行静态代码块对父类的加载以上五个步骤背下来什么时机加载某个类懒汉思想用到的时候才加载new一个类的实例调用这个类的静态方法/访问静态成员针对子类的加载也会触发父类的加载类加载加载一次即可每个类的对象在一个JVM进程中也是单例的2.双亲委派模型双亲委派模型用来找.class文件涉及到一个模块,成为类加载器,JVM中默认包含三个类加载器BootstrapClassLoaderExtensiohClassLoaderApplicationClassLoader父子关系(不是父类子类这个类加载器中有一个parent这样的属性指向父亲的引用从下往上指向父亲类似于链式结构BootstrapClassLoader负责加载JAVA标准库中的类ExtensiohClassLoader 负责加载JAVA扩展库中的类(jdk厂商做出的扩展现在已经很少使用ApplicationClassLoader负责加载第三方库以及你当前项目中的类一标准类加载步骤1)把全限定类名交给ApplicationClassLoader2) ApplicationClassloader把任务转交给父亲进行处理3)ExtensionClassLoader还是把任务转交给父亲进行处理4) BootstrapClassLoader没有父亲只能自己处理.负责扫描指定的标准库类的目录查找这个类发现能够找到因此就可以打开对应的.class文件读取文件进行解析类加载的后续步骤~~二加载第三方1)把全限定类名,交给ApplicationClassLoader2) ApplicationClassLoader 把任务转交给父亲进行处理3) ExtensionClassLoader还是把任务转交给父亲进行处理4) BootstrapClassLoader没有父亲只能自己处理.标准库中找不到对应的类因此就把任务转交给孩子5) ExtensionClassLoader 查询自己负责的目录发现也没有继续把任务交给孩子6) ApplicationClassLoader 查询第三方库的目录/当前项目的目录找到了就可以打开文件读取文件如果在这一级还没有找到抛出异常~~本质上双亲委派模型约定了“类加载的优先级“标准库最先加载其次是扩展库最后才是第三方库/当前项目垃圾回收GC应对内存泄露jvm专门指派一些线程这些线程周期的扫描你的代码已经申请的内存自动判定是否不再使用1.找出谁是垃圾在JAVA中使用对象只有一种方式就是通过引用的方式看某个对象是否引有引用指向如果没有就认为这个对象不再使用了是垃圾回收怎么判断有没有引用指向谈一谈JAVA的垃圾回收机制就可以不谈应用技术因为这是python和PHP的方案如果是问谈一谈垃圾回收机制就可以谈一下引用计数第一种方案引用计数你就是判断对方是不是垃圾引用计数给每个对象身上安排一个空间这个空间存储一个整数表示指向对象引用的个数围绕对象进行引用赋值就会更新这个数如果计数是0就可以释放但是有两个缺点也是因为这两个缺点JAVA没有引用1消耗更多内存空间如果计数器使用两个字节的整个对象本来只有四个自己这就占了50%了2产生循环引用会导致出现误判微信可达性分析微信通过可达性分析的方式从所有GCRoots出发尽可能遍历访问到的对象都标记可达剩下的就是不可达一棵树有很多个根GCRoots构成GCRoots有哪些1 栈上的局部变量栈有很多每个栈栈帧也很多每个栈帧中局部变量也有很多2 常量池引用指向对象字符串常量String也有一系列引用类型成员至少有一个 byte[]Integer值JVM把-128127这个范围的数字提前创建好Integer 对象3.所有的引用类型静态成员好的地方可达性分析没有引入额外的内存空间没有循环引用的问题弊端可达性分析的问题需要消耗更多cpu资源/更多时间这就是c加加不愿意引入gc的原因识别出垃圾之后如何释放内存1。标记清除把标记出来的垃圾直接释放掉但是对象是垃圾随机的可能会在频繁示范中出现大量的内存碎片被释放的内存在地址上是离散的(不连续)申请内存都是连续内存总空闲空间充足尝试申请大块内存申请失败2。复制算法有效解决内存碎片问题使用内存空间的一般把不是垃圾的对象复制到另一半缺点 空间利用率低如果当前存活的对象多复制开销大3。标记整理对于空间利用率得到了改善类似于顺序表删除中间元素也就是搬运把存货的数据放到非存活的数据位置开销同样可能很大对于JAVA实际情况综合上述策略构成了一个更复杂的方案4。分代回收根据不同对象的特点采取不同的方安引入对象年龄如果对象年龄比较大有很大概率继续存活下去GC扫描轮次是周期性进行的经过很多轮都没有被释放说明年龄比较大分为两个大部分新生代和老年代新生代分为伊甸区和幸存区伊甸区放刚创建的对象经过第一轮GC的时候把大部分淘汰掉经过GC没有淘汰的对象同过复制算法进入幸存区幸存区有两个部分每次只用其中一个部分下一轮GC也会针对幸存区进行扫描淘汰掉一大批对象通过复制算法进入到另一个幸存区每一轮GC淘汰很多因此出发复制的对象少解决了复制算法拷贝开销大的问题另一面幸存区只占新生代10百分之浪费内存空间小针对老年代的扫描称为 Major GC 开销大频率低针对新生代的扫描称为 Minor GC 开销小频率高这两者整体称为 FulI GC4.随着每一轮GC的进行对象就会在新生代的幸存区来回拷贝没经过一次拷贝对象的年龄就5.经过一定时间之后对象的年龄达到一定的阈值此时就会把这个对象拷贝到老年代6.对象进入老年代之后针对老年代的GC频率比新生代低很多了.减少了扫描的开销如果是某个对象占据内存很大的直接进入老年代老年代发现对象是垃圾采取标记整理的方式处理虽然整理一次比较消耗资源的 但频率比较低OK了再见