JVM小结

在这里插入图片描述
垃圾存在于堆与方法区中,栈中不会存在垃圾,所以平时说的调优就是调堆

在这里插入图片描述
在这里插入图片描述

类加载器

当程序主动使用某个类时,如果该类还未被加载到内存中,则JVM会通过加载、连接、初始化3个步骤来对该类进行初始化。如果没有意外,JVM将会连续完成3个步骤,所以有时也把这个3个步骤统称为类加载或类初始化

在这里插入图片描述

在这里插入图片描述
每个编写的".java"拓展名类文件都存储着需要执行的程序逻辑,这些".java"文件经过Java编译器编译成拓展名为".class"的文件,".class"文件中保存着Java代码经转换后的虚拟机指令,当需要使用某个类时,虚拟机将会加载它的".class"文件,并创建对应的class对象将class文件加载到虚拟机的内存,这个过程称为类加载.
《深入理解java虚拟机》中是这样说的:
Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这个过程被称为虚拟机的类加载机制

加载:类加载过程的一个阶段:通过一个类的完全限定名获取此类字节码文件(二进制字节流),并利用字节码文件创建一个java.lang.Class对象(堆中。作为方法区这个类的各种数据访问入口)java虚拟机外部的二进制字节流就按照虚拟机所设定的格式存储到方法区中
验证:目的在于确保Class文件的字节流中包含信息符合《java虚拟机规范》中的要求,保证这些信息被当作代码运行后不会危害虚拟机自身安全。主要包括四种验证,文件格式验证,元数据验证,字节码验证,符号引用验证。
准备:为类变量(即static修饰的字段变量)分配内存并且设置该类变量的初始值即0(如static int i=5;这里只将i初始化为0,至于5的值将在初始化时赋值),这里不包含用final修饰的static,因为final在编译的时候就会分配 了,注意这里不会为实例变量分配初始化,类变量会分配在方法区中,而实例变量是会随着对象一起分配到Java堆中。
解析:主要将常量池中的符号引用替换为直接引用的过程。
初始化:类加载最后阶段,若该类具有超类,则对其进行初始化,执行静态初始化器和静态初始化成员变量(如前面只初始化了默认值的static变量将会在这个阶段赋值,成员变量也将被初始化)。

双亲委派:
双亲委派模式是在Java 1.2后引入的,其工作原理的是,如果一个类加载器收到了类加载请求,
它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在
其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器,如果父
类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才
会尝试自己去加载,这就是双亲委派模式,即每个儿子都很懒,每次有活就丢给父亲去干,直到
父亲说这件事我也干不了时,儿子自己想办法去完成

二.native、方法区
Native Method stack中登记native方法,在执行引擎执行的时候加载Native Libraies【本地库】

凡是带native关键字的,说明java的作用达不到了,会去调用底层c语言的库
会进入本地方法栈
调用本地方法接口JNI
JNI:扩展java的使用,融合不同的编程语言为java所用
java诞生的时候c/c+横行,想要立足必须有调用c/c++程序
他在内存区域 中专门开辟了一块标记区域:本地方法栈,登记native方法
在最终执行的时候,加载本地方法库中的方法通过JNI
在这里插入图片描述

方法区: Method Area方法区
方法区被所有线程共享,所有的定义的方法的信息都存在于这一区域 ,此区域属于共享区间
==静态变量、lixin息【构造方法、接口定义】
、运行时常量池存在于方法区中,实例变量存在于堆内存中,和方法区无关 ==
static final class 常量池

栈:栈内存,主管程序的运行,生命周期和线程同步;
线程结束,栈内存也就是释放,对于栈来说,不存在垃圾回收问题
一旦线程结束,栈就over
栈:8大基本数据类型+对象引用+实力的方法

栈运行原理:栈帧
栈满:stackOverflowError
在这里插入图片描述
在这里插入图片描述

1.栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆
2.栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的缺乏灵活性。另外,栈数据可以共享堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。
3.基本数据如 int a = 3; long b = 255L的形式来定义(自动变量) 。自动变量存的是字面值,不是类的引用,这里并没有类的存在。如int a = 3; 这里的a是一个指向3这个字面值
这些字面值的数据,由于大小可知,生存期可知(这些字面值固定定义在某个程序块里面,程序块退出后,字段值就消失了)出于追求速度的原因,就存在于栈中
4.栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义:
int a = 3;
int b = 3;
  编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为3的地址,没找到,就开辟一个存放3这个
字面值的地址,然后将a指向3的地址。接着处理int b = 3;在创建完b的引用变量后,由于在栈中已经有3这个字面值,便将b直接指向3的地址
。这样,就出现了a与b同时均指向3的情况。
5.另一种是包装类数据,如Integer, String, Double等将相应的基本数据类型包装起来的类。这些类数据全部存在于堆中,Java用new()
语句来显示地告诉编译器,在运行时才根据需要动态创建,因此比较灵活,但缺点是要占用更多的时间
6.

String b=new String(“test”);

new了一个对象b这个对象名放在栈中,b这个对象值(对象字符串)放在堆中,"test"就会放到方法区中,这样的分工机制有效提升了程序运行的速度。

Heap,一个JVM只有一个堆,堆内存大小可以调节
类加载器读取类文件后,把类、方法、常量、即保存我们所有引用类型的实例对象
堆内存中还要细分为三个区域
新生区(伊甸园区) yong、new
养老区 old
永久区 perm
这样划分的目的是为了使 JVM 能够更好的管理堆内存中的对象,包括内存的分配以及回收
新生代:Young Generation,主要用来存放新生的对象。
老年代:Old Generation或者称作Tenured Generation,主要存放应用程序声明周期长的内存对象。
永久代:(方法区,不属于java堆,另一个别名为“非堆Non-Heap)内存的永久保存区域,主要存放Class和Meta的信息,Class在被 Load的时候被放入PermGen space区域. 它和和存放Instance的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理,所以如果你的应用会加载很多Class的话,就很可能出现PermGen space错误

1.堆大小 = 新生代 + 老年代。
2.新生代 ( Young ) = 1/3 的堆空间大小。老年代 ( Old ) = 2/3 的堆空间大小
3.默认的Edem : from : to = 8 : 1 : 1
4.JVM 每次只会使用 Eden 和其中的一块 Survivor 区域来为对象服务,所以无论什么时候,总是有一块 Survivor 区域是空闲着的。因此,新生代实际可用的内存空间为 9/10

补充:
新生代中,常规应用进行一次垃圾收集一般可以回收70%-95%的空间
回收方法区(附加补充)
永久代的垃圾收集主要回收两部分内容:废弃的常量和无用的类。
  废弃的常量:回收废弃常量与回收java堆中的对象非常类似。以常量池字面量的回收为例,假如一个字符串“abc"已经进入了常量池中,但是当前系统没有任何一个String对象是叫做"abc"的,如果这时发生内存回收,而且必要的话,这个“abc”常量就会被系统清理出常量池(注:jdk1.7及其之后的版本已经将字符串常量池从永久代中移出)
  无用的类:类需要同时满足下面3个条件才能算是“无用的类”:
该类所有的实例都已经被回收,也就是java堆中不存在该类的任何实例。
加载该类的ClassLoader已经被回收
该类对应的**java.lang.Class对象没有在任何地方被引用,**无法在任何地方通过反射访问该类的方法。

在这里插入图片描述
在这里插入图片描述

GC垃圾回收,主要在伊甸园区和养老区~ 分别对应轻GC和重GC
假设内存满了 ,OOM,堆内存不够
Java堆内存回收:
Java 中的堆也是 GC 收集垃圾的主要区域。GC 分为两种:Minor GC、Full GC ( 或称为 Major GC )。
Minor GC 是发生在新生代中的垃圾收集动作,所采用的是复制算法
  新生代几乎是所有 Java 对象出生的地方,即 Java 对象申请的内存以及存放都是在这个地方。Java 中的大部分对象通常不需长久存活,具有朝生夕灭的性质。
  
当对象在 Eden ( 包括一个 Survivor 区域,这里假设是 from 区域 ) 出生后,在经过一次 Minor GC 后如果对象还存活,并且能够被另外一块 Survivor 区域所容纳( 上面已经假设为 from 区域,这里应为 to 区域,即 to 区域有足够的内存空间来存储 Eden 和 from 区域中存活的对象 ),则使用复制算法将这些仍然还存活的对象复制到另外一块 Survivor 区域 ( 即 to 区域 ) 中,然后清理所使用过的 Eden 以及 Survivor 区域 ( 即 from 区域 ),并且将这些对象的年龄设置为1,以后对象在 Survivor 区每熬过一次 Minor GC,就将对象的年龄 + 1,当对象的年龄达到某个值时 ( 默认是 15 岁,可以通过参数 -XX:MaxTenuringThreshold 来设定 ),这些对象就会成为老年代

Full GC 是发生在老年代的垃圾收集动作,所采用的是“标记-清除”或者==“标记-整理”算法==
老年代里面的对象几乎个个都是在 Survivor 区域中熬过来的,它们是不会那么容易就 “死掉” 了的。因此,Full GC 发生的次数不会有 Minor GC 那么频繁,并且做一次 Full GC 要比进行一次 Minor GC 的时间更长

标记-清除算法收集垃圾的时候会产生许多的内存碎片 ( 即不连续的内存空间 ),此后需要为较大的对象分配内存空间时,若无法找到足够的连续的内存空间,就会提前触发一次 GC 的收集动作

永久代是属于方法区内存的部分,而新生代和老年代都是属于堆内存区域的

永久区:
这个区域常驻内存的,用来存放JDK自身携带的Class对象,Interface元数据,存储的时java运行时的一些环境或类信息~ 这个区域不存在垃圾回收,关闭JVM虚拟机就会释放这个区域的内存~

一个启动类,加载了大量第三方架包,Tomcat部署了太多应用,大量动态生成的反射类,不断被加载,直到内存满,就会出现OOM

jdk1.6之前 : 永久代 ,常量池在方法区中
jdk1.7 : 永久代 , 但是慢慢退化了,‘去永久代’ 常量池在堆中
jdk1.8 :无永久代,常量池在元空间
在这里插入图片描述
元空间:逻辑上不存在,物理上不存在
OOM:
1.尝试扩大堆内存空间
2.分析内存,(专业工具 MAT、Jprofiler)

Jprofiler作用:
分析Dump内存文件,快速定位内存泄露
获得堆中的数据

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值