java面试题集合
jvm篇
javaSource:java源代码,java类
javaClass:字节码,class类
1.java虚拟机栈内存创建main方法主线程,程序入口,java 虚拟机发现没有见过的类,触发 类加载子系统(把类所有的原始信息,加载到内存中(方法区)包含类名、方法名、成员变量、引用其他类的名字)
2.加载一个类完成后,碰到new一个对象的时候,new的对象信息存储到heap堆中(一个对象有多少成员变量,需要多少个字节多存储在堆中),而局部变量、方法参数占用的是栈内存。
3.main主线程可能被cpu切换到其他线程,使用 程序计数器,记住main主线程执行到哪行代码。而线程占用的都是栈内存。
4.GC垃圾回收被弃用的java对象
5.解释器把java字节码转换成机器码,能被cpu所使用。因为这个解释器所以java可以使用于跨平台
6.对于反复调用的代码,称之为热点代码,对于这种热点代码,使用JIT及时编译器,直接去缓存中找到相应的机器码,直接交给cpu执行。
7.程序计数器、栈内存 是线程私有的。 堆内存、方法区,是线程共享的。
8.程序计数器不会内存溢出; OutOfMemoryError溢出:①对象创建过多,一直没被GC回收②方法区内存溢出,加载的类越来越多,spring框架在运行期间动态产生新的类③ 创建线程越来越多,
长时间运行不销毁,每个线程默认最多1M的栈内存
StackOverflowError溢出:单独的线程内方法调用次数过多(例如递归调用,无线循环下去),超过1M之后,就死翘翘了
9.方法区(物理内存上其实也是堆内存的一部分):这只是定义一块内存的名称、永久代(1.8之前)、元空间(Metaspce1.8之后)是对方法区的具体实现,现在都说叫做元空间。
10.类加载器被回收之后元空间内存才会释放(系统的类加载器一般不会被释放的....)
11.jvm参数解释:(堆内存)-Xmx 最大内存 -Xms 最小内存 -Xmn 新生代占用内存(老年代占用内存=最小内存-新生代内存)
方法区内(新生代和老年代占用内存关系如下图),其中new 是新生代,新生代又包括伊甸园区、from、to区,其中from=to的内存1:1关系
eden区和from区的比例关系使用参数设置就是 -XXSurvivorRatio =8 默认是8:1
-XXNewRatio=2 新生代和老年代的内存比例,默认是2:1
服务器一般设置-Xms、-Xmx 相等以避免在每次 GC 后调整堆的大小
12.GC算法
标记:找到还在使用的对象,加上标记(根对象,一定不能被回收的,正在使用的对象和被引用的对象,静态变量),未标记的稍后会清除
①标记清除(已经弃用):两个阶段,第一阶段先标记,第二阶段直接清除,缺点是被释放的对象内存不是连续的,会有很多不连续内存,当存入较大的对象内存(数组,数组需要连续内存),还是会显示内存不够用
②标记整理:在标记清除的方法上面进行改进,在标记清除的前两个阶段多了一个整理阶段,把存活的对象,向一端靠拢,释放连续内存。缺点就是比较耗费性能,因为要移动对象,所以需要重新计算对象内存地址,还需要进行对象的复制,等等一系列操作。(老年代)
③标记复制:适用新生代,不适用老年代,新生代存活对象从from区复制到to区,然后直接清除from区的对象,非常快捷,但是占用内存较高
13.GC概述
GC的存在就是为了释放内存,减少内存碎片,速度也会提升;而且是自动释放,减少程序员的工作量,
GC回收的是堆内存,收回的对象使用的是可达性分析算法和三色标记法标记存活对象 ,回收未标记的对象
常用GC:ParallelGC (高并发,吞吐量大的很多计算的使用这个;新生代使用MinorGC ,老年代使用FullGC )、ConcMarkSweepGC(老年代并发标记,注重响应时间),G1 GC()
GC的回收规模 (小规模 常用新生代中)MinorGC (中规模处于二者之间,G1(Gone)收集器特有)MixedGC (大规模GC 堆内存占用85%左右发生,新生代+老年代全部回收)FullGC
14. 新生代GC回收
伊甸园区和幸存区(from+to)合称新生代,当伊甸园或者幸存区内存不足的时候,会采用标记复制法,清除对象,把存活的对象存储到幸存区to区中。幸存区的from区和to区每次清理后都会交换位置,重新定义from和to区
当幸存区的对象熬过几次清理之后(最多15次),这些对象会晋升到老年代(幸存区不够了,或者大对象,会直接晋升到老年代)
15.三色标记
黑色:已经标记
灰色:标记中
白色:未标记
并发漏标:解决办法增量更新法、原始快照法
类加载
类加载的三个阶段(类加载只会加载一次 )
1.加载
类的字节码载入方法区,创建.class类到堆中
如果此类的父类或者接口,没有加载,先要去加载父类、接口
类的加载是懒加载(不用的时候是不加载的)
2.链接
验证类是否符合规范、安全检查
为static 分配内存空间
将常量池中的符号解析为直接引用(这是一个很慢的过程,不是一次性完成的)
3.初始化:(懒惰)
- 静态属性:static 开头定义的属性
- 静态方法块: static {} 圈起来的方法块
- 普通属性: 未带static定义的属性
- 普通方法块: {} 圈起来的方法块
- 构造函数: 类名相同的方法
- 方法: 普通方法
调用final static修饰的基本变量,不会触发类的加载,调用final static修饰的引用变量,会触发类加载(非final静态属性,如果是final修饰的那么它在链接的第三步骤已经赋值了)
实例化:按照上面的顺序,但是不重新加载静态修饰的属性以及方法了,因为第一次初始化的时候,已经被加载过了,可以直接调用。直接运行2,3,4,5,6
普通类:
- 静态变量
- 静态代码块
- 普通变量
- 普通代码块
- 构造函数
继承的子类:
- 父类静态变量
- 父类静态代码块
- 子类静态变量
- 子类静态代码块
- 父类普通变量
- 父类普通代码块
- 父类构造函数
- 子类普通变量
- 子类普通代码块
- 子类构造函数
抽象的实现子类: 接口 - 抽线类 - 实现类
- 接口静态变量
- 抽象类静态变量
- 抽象类静态代码块
- 实现类静态变量
- 实习类静态代码块
- 抽象类普通变量
- 抽象类普通代码块
- 抽象类构造函数
- 实现类普通变量
- 实现类普通代码块
- 实现类构造函数
接口注意:
- 声明的变量都是静态变量并且是final的,所以子类无法修改,并且是固定值不会因为实例而变化
- 接口中能有静态方法,不能有普通方法,普通方法需要用defalut添加默认实现
- 接口中的变量必须实例化
- 接口中没有静态代码块、普通变量、普通代码块、构造函数
Spring框架篇
ApplicaltionContext----Refresh(共计12个方法,12 步骤)
1.prepareRefresh
创建和准备了Environment对象,Environment对象是用后续使用的提供一些键值对,1.系统环境变量中的键值对例如 JAVA_HOME ,2.java虚拟机提供的键值对,文件分隔符,默认编码
3.自定义的键值对信息,例如 springboot中的applicaion.yml中的配置信息
2.obtainFreshBeanFactory
理解 :在代码关系上AppactionContext(子类)是继承于BeanFactory,但是实际应用的时候,很多AppactionContext的很多功能并没有自己实现(创建Bena、依赖初始化、依赖注入)还是通过成员
变量BeanFactory来完成。在应用上,他们是组合关系,applicationcontext 在管理上借助了beanFactory的功能。
作用:创建BeanFactory
BeanFactory 在创建bean之前需要先知道bean的定义,这个定义是通过BeanDefinition类实现的,BeanDefinition定义Bean的单例,多例,依赖关系、初始化方式什么,那些属性需要依赖注入
beanDefinitionMap方法存储所有的BeanDefinition。BeanDefinition可以从xml文件中获取bean、可以通过配置类获得(@Bean注解)、组件扫描获得(@Service)、也可以编程添加