1.简述JVM内存模式??
- 内存私有的:虚拟机栈,本地方法栈,程序计数器
- 内存共有的:方法区,堆
简述下
- 栈:栈中存放的是栈帧,是就是方法栈,每个方法从开始到调用完成,是一次完整的入栈出栈的流程
- 程序计数器:很小的内存区域,用来记录字节码在CPU时间片所执行的位置(执行到哪里了)
- 方法区:储存被虚拟机啊加载过的一些数据,如:被虚拟机加载的类信息、常量、静态常量、即时编译器编译后的代码等数据
- 堆:几乎所有对象都在对上创建,new xxx()
2.所有的对象都在对上创建吗??
不是,符合逃逸分析的对象会在栈上分配内存。
3.逃逸分析是什么??
逃逸是指在某个方法之内创建的对象,除了在方法体之内被引用之外,还在方法体之外被其它变量引用到;这样带来的后果是在该方法执行完毕之后,该方法中创建的对象将无法被GC回收,由于其被其它变量引用。正常的方法调用中,方法体中创建的对象将在执行完毕之后,将回收其中创建的对象;故由于无法回收,即成为逃逸。
4.阻塞队列有使用过吗,简述下原理??
阻塞队列,当队列为空,出队操作会阻塞,当队列满了,入队操作会阻塞;先进先出;比喻:一个隧道有一个出口一个入口,隧道最多能容纳10辆车,当隧道内已经有10两车的时候第11辆车就要在外面等待(也就是所谓的插入阻塞);当隧道内没有车的时候,从隧道内取车的这个操作就要等待(阻塞,这里取车比喻的很勉强,哈哈,理解就行);
常用方法(一般都是成对出现)
- add:非阻塞,当队列满了的时候会抛异常,
- remove:移除方法,非阻塞,尝试从空的队列取数据是会抛异常
- off:非阻塞,队列满了 会返回false
- poll:非阻塞,尝试从空队列取数据则返回null
- put:阻塞方法,队列满了,则会阻塞
- take:阻塞方法,当队列为空时,会阻塞
常用的阻塞队列有ArrayBlockingQueue
基于数组实现、LinkedBlockingQueue
基于链表实现、PriorityBlockingQueue
优先级等等
5.GC回收了解吗??如何找到需要回收的对象??
-
引用计数法:当这个对象被引用,内部有个变量+1,取消引用-1
优点:简单 ±1 操作
缺点:两个对象相互引用,计数不为0 -
可达性分析算法:从GC ROOT开始顺着引用链依次寻找,所有节点找完后,其他不在节点上的就是垃圾。
6.常见的GC回收算法有哪几种??适用于什么场景??
- 标记清除:扫描的时候给存活的对象打个标记,扫描完后没有被标记的对象就是垃圾;
缺点:清理后的内存区域不连续,适合老年代 - 复制算法:内存换分为两个区域,每次只使用其中一半,一半满了之后将存活的对象复制到另一半内存区域上;
优点:在另一半上内存区域是连续的,适用于新生代
缺点:每次只适用一半的内存,浪费内存 - 标记整理:在标记清除基础上,增加了移动,使得不连续的内存区域变的连续了;
优点:内存连续,适用于老年代
缺点:需要暂停用户线程去做移动操作,性能有一定影响。
7.四大引用类型了解吗??
- 强引用:直接引用。例如
private User user; user=new User()
- 弱引用:使用
WeakReference
,只要发生GC就会被回收 - 软引用:使用
SoftReference
,当内存不足的时候才会被回收 - 虚引用:任何时候都可能被回收,很少用。
8.什么情况会导致内存栈溢出??
递归调用方法,最常见就是使用动态代理的时候在InvocationHandler的invoke()
里又去调用当前对象
的方法,这就属于递归去掉invoke()
方法;
//申明一个接口
interface People {
public void getName();
}
//定义一个类
class Man implements People {
@Override
public void getName() {
System.err.println("要");
}
}
调用
final People man = new Man();
People people = (People) Proxy.newProxyInstance(man.getClass().getClassLoader(), man.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//注意:在这里如果调用proxy的方法就会栈溢出
((People) proxy).getName();
System.err.println("我");
Object o = method.invoke(man, args);
System.err.println("好好学习");
return o;
}
});
people.getName();
运行就会抛出java.lang.StackOverflowError: stack size 8192KB
内存栈溢出
8.final,finally,finalize区别??
- final:修饰类则该类不能被继承,修饰变量,变量不能被修改;
- finally:try/catch finally不用多说吧
- finalize:属于对象Object的方法,在对象回收后可能会被调用;注意是可能,在这里做回收操作不可取;
9.String s=new String("xx);创建了几个对象??
- 第一个是new String() 的String对象;
- 第二是“xx”对象,他存在于常量池中