Java面试
一、基础
1.&&和&的区别? |和||的区别?
1.当符号左边是false时,&继续执行符号右边的运算。&&不再执行符号右边的运算。
2.当符号左边是true时,|继续执行符号右边的运算,而||不再执行符号右边的运算
2.final关键字的作用
fianl修饰类时,类不能被继承
fianl修饰方法时,方法不能被重写
fianl修饰变量时,变量不能被修改。他就变成常量了
2+final finally finalize区别
finally
是异常处理语句结构的一部分,表示总是执行,try{}catch(){}finally{}
finalize
是Object类的一个方法,在垃圾收集器执行的时候会调用被回收的对象的finalize方法,供垃圾收集时的其他资源回收,例如关闭文件等
finalize()方法在垃圾收集器将对象从内存中清除出去前,做必要的清理工作
3.static静态关键字的作用?
被static所修饰的是静态变量,静态变量由多个对象共享,如果修改了静态变量的值,那么其他对象的静态变量也会被修改
static修饰的方法称为静态方法,可以类名.方法名直接进行调用,不用依赖对象实例也可以进行调用。因为static方法独立于任何实例,因此static方法必须被实现,所以不能是抽象的abstract方法
被static修饰的代码块称为静态代码块,会随着java加载类的时候加载这些代码块。他们可以有多个。可以放在类中的任何位置,但是只能初始化一次
4.this关键字,super关键字的用法?
this表示当前对象的引用
- this.方法() 调用当前对象中的方法
- 形参成员重名,用this来区分
- 引用本类的构造方法
super表示父类对象的引用
作用:调用父类的方法、属性、构造器
5.面向对象编程有哪些特点
面向对象三大特征:封装、继承、多态
封装: 通常认为,封装是把数据和操作数据的方法绑定起来,对数据的访问只能通过已定义的接口,封装就是隐藏一切可隐藏的东西,只向外界提供简单的接口
继承: 继承是从已有的类中继承信息创建新类的过程,提供继承信息的类是父类,得到继承信息的类是子类。
多态:多态是指允许不同子类型的对象对同一消息做出不同的响应。(同样的对象调用同样的方法做出了不同分响应)多态分为编译时多态和运行时多态。多态的前提是继承,方法的重写,向上造型
6.子类继承父类时父类的构造方法何时调用
在子类new对象的时候会先调用父类的构造方法,然后再调用子类的构造方法。
7.面向过程与面向对象有什么区别
面向过程就是分析出解决问题所需要的步骤,然后使用函数把这些步骤一步一步的实现,使用的时候一个一个的调用即可。
面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描述某个事物在整个解决问题的步骤中的行为。
8.什么是Java的跨平台性?简述其原理
所谓的跨平台性,是指Java语言编写的程序,一次编译之后可以在多个平台上运行。
原理:只需要在要运行Java代码的系统上安装一个JVM即可。由JVM负责Java程序在该系统中的运行。所以Java可以跨平台
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7cNRq3tE-1629779155626)(file:///F:/%E6%A1%8C%E9%9D%A2/Gaoven/%E8%AF%BE%E4%BB%B6/Java%E5%9F%BA%E7%A1%80/01-05.files/image006.jpg)]
9.break、continue、return的区别和作用
break:结束当前循环
continue:跳出本次循环,继续执行下次循环
return:程序返回,不再执行下面的代码
10.i++和++i的区别?
这两个都是自增的操作
i++ 是先使用后自增
++i 是先自增在使用
比如:
int i=1;
int j=i++;
return j;
j=1
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
int i=1;
int j=++i;
return j;
j=2
11.JDK、JRE、JVM有什么区别
JVM=java虚拟机
JRE=Java虚拟机+核心类库 ----------- 是运行Java程序的环境
JDK=Java虚拟机+核心类库 +编译工具
12.列出JAVA中所有基本数据类型,并说明这些数据类型占用的字节数?
Java中的基本数据类型:(八大基本类型)
整型 : byte、short、int、long
浮点型 : float、double
字符型:char
布尔型:boolean
所占字节数:
byte :1字节
short: 2字节
int : 4字节
long : 8字节
float : 4字节
double: 8字节
char: 2字节
boolean: 1字节
13.静态变量和实例变量的区别?
静态变量前要加static 关键字,而实例变量前则不加。
-
实例变量是某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。
-
静态变量不属于某个实例对象,而是属于类,所以也称为类变量。
-
实例变量必须创建对象后才可以通过这个对象来使用,静态变量则可以直接使用类名来引用。
14.abstract class和interface有什么区别?
abstract class称为抽象类。
什么是抽象类?
我们把用abstract修饰的方法,称为抽象方法。抽象方法没有方法体。而我们把含有抽象方法的类称为抽象类,必须用abstract修饰,这就是 abstract class
interface称为接口
什么是接口?
接口就是特殊的抽象类,里面所有的方法都是抽象方法,没有一般方法。但是在jdk1.8版本后推出了接口中也可以有一般方法,需要用default ,static修饰
二者的区别:
- 实现:抽象类的子类使用 extends 来继承;接口必须使用 implements 来实现接口。
- 构造函数:抽象类可以有构造函数;接口不能有。
- main 方法:抽象类可以有 main 方法,并且我们能运行它;接口不能有 main 方法。
- 实现数量:类可以实现很多个接口;但是只能继承一个抽象类。
- 访问修饰符:接口中的方法默认使用 public 修饰;抽象类中的方法可以是任意访问修饰符。
14+抽象类能被实例化么?
抽象类其实可以被实例化,但是实例化的方式不是通过new方式来创建对象的,而是通过父类应用指向子类的实例间接地实现父类的实例化,因为在实例化子类之前,一定会实现他的父类。
14++抽象类为什么不能new
- 抽象类没有方法体,无法生成一个不具体的对象
- 对象实例化的时候,关键字new向JVM申请内存,这个类的成员(成员变量,成员方法)会被保存到内存中。而抽象类没有具体的成员(成员变量,成员方法),没办法准确分配内存
- 抽象类是为了实现多态,当某些类只希望作为父类使用,不希望被实例化。当我们进行设计的时候需要尽量依赖父类,越向上层的类越稳定,不容易被实例化
- 实例化和有没有构造方法没有关系,构造方法只是初始化对象的,new关键字向JVM申请内存来创建对象的。
15.重写和重载的区别
方法的重载和重写都是多态的实现方式,区别在于重载是编译时多态,重写是运行时多态
重载发生在同一个类中,同名的方法有着不同的参数列表被视为重载
重写发生在子类与父类之间,重写要求子类重写方法与父类的被重写 方法有相同的返回值类型,访问权限大于父类方法
区别:
1.参数列表
重写要求参数列表与被重写方法完全一致
重载要求参数列表中参数的顺序、类型、个数完全不同
2.访问权限,方法的修饰符
重写要求方法权限大于等于被重写方法
重载要求方法名相同即可,没有权限限制
3.异常
重写不能抛出父类没有的一般异常
只能抛出非运行时异常
4.前提
重写的前提是继承,有父子级关系
重载没有前提
16. ==和equals()的区别?
== : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象(基本数据类型比较的是值,引用数据类型比较的是内存地址)。
equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:
情况1:类没有覆盖写 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象。
情况2:类override了 equals() 方法。一般,我们都覆盖 equals() 方法来比较两个对象的内容是否相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。
17.throw和throws的区别
throw 表示抛出一个异常类的对象,生成异常对象的过程。声明在方法体内。
throws 属于异常处理的一种方式,声明在方法的声明处。
18.collection 和collections
clloections是java.util下面的类,它的里面有各种相关集合操作的方法
collection是接口,是各种集合结构的父接口。
19.String类能被继承吗为什么?
不能被继承,因为String类是由fianl修饰的,final修饰的类不能被继承
20.为什么用fianl修饰String?
1.为了实现常量池,节约内存
String有专门的字符串常量池,只有用fianl修饰才能实现常量池。
常量池的存在,没有使用new关键字而产生的String对象,会存储到字符串常量池,当然如果有同样的内容的字符串产生时,将不会再新建对象,直接从常量池中取出来节省了堆内存空间。因为jvm里面,堆是用来存放对象实例的地方。
2.多线程安全
String被fianl修饰后是多线程安全的。同一字符串可以被多个线程共享,多个线程无法改变字符串内容。这就避免了当多个对象指向同一String时如果有一个对象改变了String会对其他的对象产生影响。所以安全。
3.实现String可以创建hashcode不可变性
因为字符串是不可变的,因为他在创建的时候hashcode就被缓存了,不需要重新计算,这就使得字符串很适合做map中的key,字符串的处理速度要快过其他键对象,所有hashmap中往往使用string作为键
21.new()和clone()的区别
A a=new A();
new对象的过程:程序执行到new操作符的时候会看new操作符后面的类型,因为知道了类型才知道分配多大的内存空间。分配完内存空间再调用构造函数填充对象的各个域进行对象的初始化。构造方法返回后,一个对象创建完毕,可以把她的引用地址发送到外部,外部使用这个引用操作对象。
A a1=(A)a.clone()
克隆的第一步与new相似都是分配内存空间。调用克隆方法时分配内存空间与原对象相同,然后使用原对象的各个域填充克隆对象的各个域。clone()返回,一个新的对象就被创建了,内容相同,地址值不同。
22.对象类中如何比较对象中某个值的大小进行排序?
重写Comparable接口和Comparator中的方法
23.Integer值超过127之后发生了什么?
Integer.class在装载(Java虚拟机启动)时,其内部类型IntegerCache的static块即开始执行,
实例化并暂存数值在-128到127之间的Integer类型对象。
当自动装箱int型值在-128到127之间时,即直接返回IntegerCache中暂存的Integer类型对象。
-128-127在缓存里取。否则就返回一个新对象。对象的地址值就不相同了,一个在静态区,一个在对象区
24.什么是浅拷贝和深拷贝?
浅拷贝(Shallow Copy):指向被复制的内存地址,如果地址发生改变,浅拷贝出来的对象也会相应改变。
深拷贝:为对象都开辟了新的内存空间地址,使这个增加的指针指向新的内存
总结:实则浅拷贝和深拷贝只是相对的,如果一个对象内部只有基本数据类型,那用 clone() 方法获取到的就是这个对象的深拷贝,而如果其内部还有引用数据类型,那用 clone() 方法就是一次浅拷贝的操作。
25…什么是序列化与反序列化?
作用:在网络上传输对象字节序列,把对象字节保存到硬盘上。减少内存消耗。或者在远程rpc调用对象的时候需要。
序列化就是把对象存储到硬盘上,反序列化就是获取对象文件中的数据,转成后台对象类的数据。
26.说一下JDBC链接数据库的过程
-
加载驱动
Class.forName("com.mysql.jdbc.Driver"); 原来是DriverManager.registerDriver(new com.mysql.jdbc.Driver()); driver底层已经帮忙注册了 public Driver()throws SQLException{ } static{ try{ DriverManager.registerDriver(new Driver()); }catch(SQLException var){ throw new RuntimeException("Can't register driver!") } }
-
获取连接
Connection con=DriverManager.getConnection(url,name,pwd);
-
获取传输器
Statement st=con.createStatement(); String sql="insert usered('id','name','password') values(?,?,?)" preparedStatement ps=con.prepareStatement(sql);//预编译SQL //手动给参数赋值 ps.setInt(1,6); ps.setString(2,"小棉袄"); ps.setString(3,"123");
-
执行sql
Result rs=st.executeQuery(sql);
-
获取结果集,处理结果集
while(rs.next()){ System.out.println(rs.getObject(""))}
-
关闭资源
DBCP-C3P0连接池
数据库连接-执行完毕-释放,连接释放十分浪费系统资源
池化技术:准备一些预先的资源,过来就连接预先准备好的
最小连接数:10
最大连接数:15
等待超时:100ms
编写连接池,实现一个接口DataSource
开源数据源实现
DBCP
导入jar包到lib
src下创建文件dbcpconfig.properties
C3P0
导入jar到lib
src下配置c3p0-config.xml文件
DBCP和C3P0的区别:
C3P0有自动回收空闲连接的功能,DBCP没有
结论:无论使用什么数据源,本质还是一样,DataSource接口不会变,方法就不会变
Druid:阿里巴巴
27.java 中操作字符串都有哪些类?它们之间有什么区别?
操作字符串的类有:String、StringBuffer、StringBuilder。
String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。
StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。
28.String str="i"与 String str=new String(“i”)一样吗?
不一样,因为内存的分配方式不一样。String str="i"的方式,java 虚拟机会将其分配到常量池中;而 String str=new String(“i”) 则会被分到堆内存中。
29.String 类的常用方法都有那些?
- indexOf():返回指定字符的索引。
- charAt():返回指定索引处的字符。
- replace():字符串替换。
- trim():去除字符串两端空白。
- split():分割字符串,返回一个分割后的字符串数组。
- getBytes():返回字符串的 byte 类型数组。
- length():返回字符串长度。
- toLowerCase():将字符串转成小写字母。
- toUpperCase():将字符串转成大写字符。
- substring():截取字符串。
- equals():字符串比较
一Plus前端
1.浏览器发出一个请求到收到响应经历了哪些步骤?
- 浏览器解析用户输入的URL,生成一个HTTP格式的请求
- 先根据URL域名从本地hosts文件中查找是否有映射IP,如果没有就将域名发送给电脑所配置的DNS进行域名解析,得到IP地址
- 浏览器通过操作系统将请求通过四层网络协议发送出去
- 途中可能会经过各种路由器、交换机,最终到达服务器。
- 服务器收到请求后,根据请求所指定的端口,将请求传递给绑定了该端口的应用程序,比如8080被Tomcat占用了
- Tomcat接收到请求数据后,按照HTTP协议的格式进行解析,解析得到所要访问的servlet
- 然后servlet来处理这个请求,如果是springMVC中的DispatchServlet,那么会找到对应的Controller中的方法,并执行该方法得到结果
- Tomcat得到响应结果后封装成HTTP响应的格式,并再次通过网络发送给浏览器所在的服务器
- 浏览器所在的服务器拿到结果后再传递给浏览器,浏览器则负责解析并渲染
2.跨域请求是什么?有什么问题?如何解决?
跨域请求是指浏览器在发起网络请求时,会检查该请求所对应的的协议、域名、端口和当前网页是否一致,如果不一致则浏览器会进行限制,比如在www.baidu.com的某个网页中,如果使用ajax去访问www.jd.com是不行的,但如果是img,ifram,script等标签的src属性去访问则是可以的,之所以浏览器要设置这层限制,是为了用户信息安全,但是如果开发者想要绕过这层限制也是可以的:
- response添加header,被逼入resp.setHeader(“Access-Control-Allow-Origin”,"*")表示可以访问所有网站,不受是否同源的限制
- jsonp的方式,该技术底层就是基于script标签来实现的,因为script标签是可以跨域的
- 后台自己控制,先访问同域名下的接口,然后在接口中再去使用HTTPClient等工具去调用目标接口
- 网关,和第三种方式类似,都是交给后台服务来进行跨域访问
二、JVM
数据区
jvm中那些是线程共享区
堆区和方法区是所有线程共享的,栈,本地方法栈,程序计数器是每个线程独有的
1.Java程序运行机制详细说明
- 首先利用IDE集成开发工具编写Java源代码,源文件的后缀为.java;
- 再利用编译器(javac命令)将源代码编译成字节码文件,字节码文件的后缀名为.class;
- 运行字节码的工作是由解释器(java命令)来完成的。
java文件通过编译器变成了.class文件,接下来类加载器又将这些.class文件加载到JVM中。
其实可以一句话来解释:类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个 java.lang.Class对象,用来封装类在方法区内的数据结构。
2.JVM运行时数据区
1、堆
JVM中最大的一块,主要用来存放对象实例和数组,几乎所有的对象实例都在这里分配内存。线程共享,内部会划分出多个线程私有的分配缓冲区(TLAB)。可以位于物理上不连续的空间,但是逻辑上要连续。
2、虚拟机栈
每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。线程私有,生命周期和线程一致。
3、方法区(非堆)
属于共享内存区域,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
4、本地方法栈
本地方法栈(Native MethodStacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java 方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native 方法服务。
5、程序计数器
程序计数器(Program CounterRegister)是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。此内存区域是唯一一个在Java 虚拟机规范中没有规定任何 OutOfMemoryError 情况的区域
3.说说堆和栈的区别
1)物理地址
堆的物理地址分配对对象是不连续的。因此性能慢些。在GC的时候也要考虑到不连续的分配,
所以有各种算法。比如,标记-消除,复制,标记-整理,分代(即新生代使用复制算法,老年
代使用标记——压缩)
栈使用的是数据结构中的栈,先进后出的原则,物理地址分配是连续的。所以性能快。
2)内存分配
堆因为是不连续的,所以分配的内存是在运行期确认的,因此大小不固定。一般堆大小远远大
于栈。
栈是连续的,所以分配的内存大小要在编译期就确认,大小是固定的。
3)存放的内容
堆存放的是对象的实例和数组。因此该区更关注的是数据的存储
栈存放:局部变量,操作数栈,返回结果。栈区更关注的是程序方法的执行。
4)程序的可见度
堆对于整个应用程序都是共享、可见的。
栈只对于线程是可见的。所以也是线程私有。他的生命周期和线程相同。
4.引用类型
对象引用类型分:强引用、软引用、弱引用和虚引用。
强引用:不会被回收。就是我们一般声明对象是时虚拟机生成的引用,强引用环境下,垃圾回收时需要严格判断当前对象是否被强引用,如果被强引用,则不会被垃圾回收。
软引用:内存充足,则不回收;不足则回收。软引用一般被做为缓存来使用。与强引用的区别是,软引用在垃圾回收时,虚拟机会根据当前系统的剩余内存来决定是否对软引用进行回收。如果剩余内存比较紧张,则虚拟机会回收软引用所引用的空间;如果剩余内存相对富裕,则不会进行回收。换句话说,虚拟机在发生OutOfMemory时,肯定是没有软引用存在的。
弱引用:会被回收。弱引用与软引用类似,都是作为缓存来使用。但与软引用不同,弱引用在进行垃圾回收时,是一定会被回收掉的,因此其生命周期只存在于一个垃圾回收周期内。
GC
主流的有三种垃圾回收算法:复制算法,标记-清除算法、标记-整理算法
1.JVM中那些可以作为gc root
gc root,特征,就是她指挥引用其他对象,而不会被其他对象引用,例如栈中的本地变量,方法区中的静态变量,本地方法栈中的变量,正在运行的线程等可以作为gc root
2.项目中如何排查JVM问题
对于还在正常运行的系统:
- 可以使用jmap来查看JVM中各个区域的使用情况
- 可以通过jstack来查看线程的运行情况,比如那些线程阻塞,是否出现死锁
- 可以通过jstat命令来查看垃圾回收的情况,特别是fullgc,如果发现fullgc比较频繁,那就得进行调优了
- 通过各个命令的结果,或者jvisualvm等工具来进行分析
- 首先,初步猜测频繁发生fullgc的原因,如果频繁发生fullgc但是又一直没有出现内存溢出,那么表示fullgc实际上是回收了很多对象了,所以这些对象最好能在younggc过程中就直接回收,避免这些对象进入到老年代,对于这种情况,就要考虑这些存货时间不长的对象是不是比较大,导致年轻代放不下,直接进入到老年代,尝试加大老年代的大小,如果改完后,fullgc减少,则证明修改有效。
- 同时,还可以找到占用CPU最多的线程,定位到具体的方法,优化这个方法的执行,看是否能避免某些对象的创建,从而节省内存
对于已经发生OOM的系统:
- 一般生产系统都会设置当系统发生OOM时,生成当时的dump文件
- 可以用jsisualvm等工具来分析dump文件
- 根据dump文件找到异常的实例对象,和异常的线程(占用CPU高),定位到具体的代码
- 然后在进行详细的分析和调试
总之调优不是一蹴而就的,需要分析、推理、实践、总结、再分析,最终定位到具体的问题
5.JVM有多种垃圾回收算法,其中目前在用最经典的就是分代收集算法。
永久代(Perm):主要保存class,method,field等对象,该空间大小,取决于系统启动加载类的数量,一般该区域内存溢出均是启动时溢出。java.lang.OutOfMemoryError: PermGen space
老年代(Old):一般是经过多次垃圾回收(GC)没有被回收掉的对象。
伊甸园(Eden):新创建的对象。
幸存区0(Survivor0):经过垃圾回收(GC)后,没有被回收掉的对象。
幸存区1(Survivor1):同Survivor0相同,大小空间也相同,同一时刻Survivor0和Survivor1只有一个在用,一个为空。
6.标记-清除算法
首先标记出所有需要回收的对象,标记完成后统一回收所有被标记的对象。缺点:标记和清除两个过程效率都不高;标记清除后会产生空间碎片,空间碎片导致分配较大对象时可能提前触发垃圾回收。
7.复制算法
将可用内存分为两个区域,每次只使用其中一块,当使用的那一块内存用完时,将还存活的对象复制到另外一块内存中,然后把已使用过的内存空间一次清理掉。优点:解决的空间碎片问题,实现简单。缺点:需要两倍空间,将内存缩小为两块,内存使用率不高。复制操作频繁效率变低。
8.标记-整理算法
此算法结合了“标记-清除”和“复制”两个算法的优点。也是分两阶段,第一阶段从根节点开始标记所有被引用对象,第二阶段遍历整个堆,把清除未标记对象并且把存活对象“压缩”到堆的其中一块,按顺序排放。此算法避免了“标记-清除”的碎片问题,同时也避免了“复制”算法的空间问题。
9.垃圾收集器CMS G1
垃圾回收策略可以看作是内存回收的抽象策略,而垃圾收集器是内存回收的具体实现
垃圾收集器有很多种,常见的有:串行收集器、并行收集器、并发收集器、CMS收集器以及最新的G1收集器。重点为CMS收集器和G1收集器
10.CMS收集器
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器
基于 标记清除 算法实现。第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。
特点:
针对老年代
基于"标记-清除"算法(不进行压缩操作,会产生内存碎片)
以获取最短回收停顿时间为目标
并发收集、低停顿
需要更多的内存
运作步骤:
初始标记: 暂停所有的其他线程,标记GC Roots能直接关联到的对象,速度很快;
并发标记:进行GC Roots Tracing的过程;
重新标记: 修正并发标记期间的变动部分,需要"Stop The World",且停顿时间比初始标记稍长,但远比并发标记短;
并发清除: 开启用户线程,同时GC线程开始对为标记的区域做清扫,回收所有的垃圾对象。
缺点:
对 CPU 资源敏感;
无法收集浮动垃圾;
标记清除 算法带来的空间碎片。
11.G1收集器
G1 (Garbage-First)是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器。以极高概率满足GC停顿时间要求的同时,还具备高吞吐量性能特征。
G1是将整个堆空间分成许多个大小不等的独立区域(Region),大约有2000块,每个Region从1M到32M大小不等,在JVM启动的时候就已经分割好了,Region可采用并行的垃圾回收或 NOT STW 方式。
运作步骤:
初始标记(Initial Marking)
并发标记(Concurrent Marking)
最终标记(Final Marking)
筛选回收(Live Data Counting and Evacuation)
特点:
1、并行与并发
G1能充分利用CPU、多核环境下的硬件优势,使用多个CPU(CPU或者CPU核心)来缩短stop-The-World停顿时间。部分其他收集器原本需要停顿Java线程执行的GC动作,G1收集器仍然可以通过并发的方式让java程序继续执行。
2、分代收集
虽然G1可以不需要其他收集器配合就能独立管理整个GC堆,但是还是保留了分代的概念。
能独立管理整个GC堆(新生代和老年代),而不需要与其他收集器搭配;
能够采用不同方式处理不同时期的对象;
虽然保留分代概念,但Java堆的内存布局有很大差别;
将整个堆划分为多个大小相等的独立区域(Region);
新生代和老年代不再是物理隔离,它们都是一部分Region(不需要连续)的集合。
3、空间整合
与CMS的“标记–清理”算法不同,G1从整体来看是基于“标记整理”算法实现的收集器;从局部上来看是基于“复制”算法实现的。是一种类似火车算法的实现,不会产生内存碎片,有利于长时间运行。
4、可预测停顿
这是G1相对于CMS的另一个大优势,降低停顿时间是G1和CMS共同的关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型。可以明确指定M毫秒时间片内,垃圾收集消耗的时间不超过N毫秒。在低停顿的同时实现高吞吐量。
为什么G1可以实现可预测停顿?
可以有计划地避免在Java堆的进行全区域的垃圾收集;
G1收集器将内存分大小相等的独立区域(Region),新生代和老年代概念保留,但是已经不再物理隔离。
G1跟踪各个Region获得其收集价值大小,在后台维护一个优先列表;
每次根据允许的收集时间,优先回收价值最大的Region(名称Garbage-First的由来);
类加载器
12.类加载器,加载类的流程
类加载器实现的功能是:为加载阶段获取二进制字节流
1、加载
加载主要是将.class文件(并不一定是.class。可以是ZIP包,网络中获取)中的二进制字节流读入到JVM中。
在加载阶段,JVM需要完成3件事:
通过类的全限定名获取该类的二进制字节流;
将字节流所代表的静态存储结构转化为方法区的运行时数据结构;
在内存中生成一个该类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
2、连接
2.1 验证
验证阶段,确保加载进来的字节流符合JVM规范。将完成以下4个阶段的检验动作:
文件格式验证
元数据验证(是否符合Java语言规范)
字节码验证(确定程序语义合法,符合逻辑&#