野原广志

发布时间:2020-05-16

关注作者
转载请打上链接

JVM概述

JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。

JVM的内存结构(重点)

首先类加载器(class loader

​ 作用:首先它是负责加载类文件,并且把它放到方法区中去

有多少种类加载器呢(共有四种

1、根类加载器(BootStrapClassLoader), 主要负责加载jre/lib/rt.jar相关的字节码文件的。

2、扩展类加载器(ExtensionClassLoader), 主要负载加载 jre/lib/ext/*.jar 这些jar包的。 该类加载器在JDK1。9的时候更名为: Platform Class Loader, 其父类加载器为: null。

3、应用程序类加载器(ApplicationClassLoader), 主要负责加载用户自定义的类以及classpath环境变量所配置的jar包的。 该类加载器在JDK1.9的时候更名为: System ClassLoader, 其父类加载器为: ExtensionClassLoader。

4、自定义类加载器(UserClassLoader), 负责加载程序员指定的特殊目录下的字节码文件的。大多数情况下,自定义类加载器只需要继承ClassLoader这个抽象类,重写findClass()和loadClass()两个方法即可。

类加载的过程

加载,连接,验证,准备,解析,初始化

类的加载是指把类的.class文件中的数据读入到内存中,通常是创建一个字节数组读入.class文件,然后产生与所加载类对应的Class对象。加载完成后,Class对象还不完整,所以此时的类还不可用。当类被加载后就进入连接阶段,这一阶段包括验证、准备(为静态变量分配内存并设置默认的初始值)和解析(将符号引用替换为直接引用)三个步骤。最后JVM对类进行初始化,包括:

1\如果类存在直接的父类并且这个类还没有被初始化,那么就先初始化父类;

2\如果类中存在初始化语句,就依次执行这些初始化语句。

java类加载器的类加载机制

总共有三种

1、全盘加载

2、缓存机制

3、双亲委派机制

​ 类加载器收到类加载的请求,它会将这个请求向上一层加载器(父类加载器)委派,一直到启动类加载器,如果启动类加载器可以加载这个类,那么类加载就到此结束,并一步一步返回到子类加载器,如果没有就会报错。

作用:

1、防止加载同一个class文件,通过委托上一层询问结果,没加载就加载,加载过就不用加载了,确定了数据的安全性。

2、保护了核心类class文件不被篡改,通过委派的方式,即使被篡改了也不会去加载它,即使加载了也不属于同一个class文件了不同的加载器加载同一个.Class也不是同一个.Class对象,这样保证了Class执行安全。

双亲委派机制可以被违背嘛?

可以被违背,当加载我们自己的classloader类加载器的时候,或者使用线程上下文类加载器的时候

有三次被破坏

继承ClassLoader这个抽象类,重写findClass()和loadClass()两个方法即可破坏双亲委派机制

http://yanu.net/post/28

扩展tomcate的类加载机制

Tomcat的类加载机制是违反了双亲委托原则的,对于一些未加载的非基础类(Object,String等),各个web应用自己的类加载器(WebAppClassLoader)会优先加载,加载不到时再交给commonClassLoader走双亲委托。


沙箱安全机制

什么是沙箱安全机制?

Java安全模型的核心就是Java沙箱(sandbox),什么是沙箱?沙箱是一个限制程序运行的环境。沙箱机制就是将 Java 代码限定在虚拟机(JVM)特定的运行范围中,并且严格限制代码对本地系统资源访问,通过这样的措施来保证对代码的有效隔离,防止对本地系统造成破坏。沙箱主要限制系统资源访问,那系统资源包括什么?——CPU、内存、文件系统、网络。不同级别的沙箱对这些资源访问的限制也可以不一样。

  所有的Java程序运行都可以指定沙箱,可以定制安全策略。

方法区

方法区与元空间

	方法区与元空间和永久代三者的关系很难
	很多文章里喜欢把方法区等同与永久代,永久代既然没了,方法区也就没了。
	永久代指物理上的堆内存的一块空间,这块实际的空间完成了方法区存储字节码、静态变量、常量的功能等等。
	现在元空间也可以认为是新的方法区的实现了。
	方法区也叫永久代。在过去(自定义类加载器还不是很常见的时候),类大多是”static”的,很少被卸载或收集,因此被称为“永久的(Permanent)”。虽然Java 虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做Non-Heap(非堆),目的应该是与Java 堆区分开来。同时,由于类class是JVM实现的一部分,并不是由应用创建的,所以又被认为是“非堆(non-heap)”内存。HotSpot 虚拟机的设计团队选择把GC 分代收集扩展至方法区,或者说使用永久代来实现方法区而已。对于其他虚拟机(如BEA JRockit、IBM J9 等)来说是不存在永久代的概念的。
	永久代也是各个线程共享的区域,它用于存储已经被虚拟机加载过的类信息,常量,静态变量(JDK7中被移到Java堆),即时编译期编译后的代码(类方法)等数据。这里要讲一下运行时常量池,它是方法区的一部分,用于存放编译期生成的各种字面量和符号引用(其实就是八大基本类型的包装类型和String类型数据(JDK7中被移到Java堆))

1、方法区属于线程共享的,所以他是安全的

2、方法区是堆的一个逻辑部分,他还有另一个称呼非堆(Non-Heap),目的是于java堆区分开来。

3、可以选择不被垃圾回收的,

4、用以存储已被虚拟机加载过的类信息、常量、静态变量、即时编译器编译后的代码等

è¿éåå¾çæè¿°

5、和堆一样不需要连续的内存

native方法区

​ 凡是带了native关键字的方法就代表java的作用范围就达不到了

​ 带有native的方法会调用JNI,最后去加载C++

栈(Java Virtual Machine Stacks)

1、方法执行的内存模型,线程私有

2、先进后出,后进先出

队列:先进先出(FIFO:Frist Input Frist OutPut)

总结方言:喝多了吐就是栈,吃多了拉就是列

栈内存:主管程序的运行,生命周期和线程同步

线程结束,栈内存也就释放了,对于栈来说,不存在垃圾回收问题

一旦线程结束,栈就over了

栈运行原理:栈帧(Stack Frame)

简单类对象的实例化过程

简单地说,栈帧就是一个方法,里面有输入输出参数,局部变量表,返回值等信息,第一个参数一定是this

  1. 在方法区先加载父类,在加载子类
  2. 在栈中申请空间,声明变量P
  3. 在堆内存中开辟空间,分配对象地址
  4. 在对象空间中,对对象的属性(包括父类的属性)进行默认初始化
  5. 子类构造方法进栈

存放创建好的对象和数组(数组也是对象)(new出来的对象存放在堆中)

JVM中只有一个堆空间,它被所有线程共享

堆是一个不连续的内存空间,分配灵活,速度慢!

Heap,一个JVM只有一个堆内存,堆内存的大小是可以调节得。

类加载器读取了类配置文件后,一般把什么放进队中?

堆简单分为三个区域:

  • 伊甸园(Eden Space)
  • 幸存区(分为01区)
  • 养老区
  • 永久存储区(元空间)

GC垃圾回收,主要是在伊甸园区和养老区中间

如果堆neicun满了,就会报OOM(OutOfMemoryError:java heap space)错误,堆内存不够

在jdk1.8后,永久存储区改了一个名字(元空间);

新生区
  • 类的诞生
  • 伊甸园(eden space)如果满了就触发一次 轻GC
  • 幸存区1
  • 幸存区2 将会出发重GC或

真理,经过研究,99%的对象都是临时对象

永久区

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

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

  • jdk.1.6之前 :永久代,常量池实在方法区
  • jdk1.7 :永久代,但是慢慢退化了,去永久代,常量池在堆中
  • jdk1.8之后 :无永久代,常量池在元空间

在一个项目中,突然出现了OOM故障,那么如何排除~研究为什么出错

  • 能够看到代码第几行出错:内存快照分析工具,MAT,Jprofiler
  • Dubug,一行行分析代码

MAT,Jprofiler作用:

  • 分析Dump内文件xmj
  • xiemingjin

JVM在进行GC时,并不是对者三个区域统一回收。大部分时新生代

新生代

幸存区(form,to)

老年代

GC两种类:轻GC,重GC(全局GC)

JVM的内存模型和分区,每个区放什么

堆里面的分区有哪些

引用清除算法

程序计数器

程序计数器(Program Counter Register)是一块较小的内存空间,可以看作是当前线程所执行字节码的行号指示器,指向下一个将要执行的指令代码,由执行引擎来读取下一条指令。更确切的说,一个线程的执行,是通过字节码解释器改变当前线程的计数器的值,来获取下一条需要执行的字节码指令,从而确保线程的正确执行。

为了确保线程切换后(上下文切换)能恢复到正确的执行位置,每个线程都有一个独立的程序计数器,各个线程的计数器互不影响,独立存储。也就是说程序计数器是线程私有的内存。

如果线程执行 Java 方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果执行的是 Native 方法,计数器值为Undefined。

程序计数器不会发生内存溢出(OutOfMemoryError即OOM)问题。

评论

  • 张三: 回复信息:

    你这各写的优点很大的问题呀你这各写的优点很大的问题呀

返回顶部
尾巴