对象一定分配在堆中吗?有没有了解逃逸分析技术?
- 不一定的,JVM 通过「逃逸分析」 ,那些逃不出方法的对象会在栈上分配.
逃逸分析是指分析指针动态范围的方法,它同编译器优化原理的指针分析和外形分析相关联。当
变 量(或者对象)在方法中分配后,其指针有可能被返回或者被全局引用,这样就会被其他方法
或者 线程所引用,这种现象称作指针(或者引用)的逃逸(Escape)。通俗点讲,如果一个对象
的指针被 多个方法或者线程引用时,那么我们就称这个对象的指针发生了逃逸。
volatile:可见性、顺序性
保证并发编程的三要素(可见性、顺序性、原子性)之二,可结合AtomicInteger等对象使用
volatile 本质是在告诉 jvm 当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读 取
使用 volatile 一般用于 状态标记量 和 单例模式的双检锁。
1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对 其他线程来说是立即可见的,volatile 关键字会强制将修改的值立即写入主存。
2)禁止进行指令重排序。
x = 2;//语句 1
y = 0;//语句 2
flag = true;//语句 3
x = 4;//语句 4
y = -1;//语句 5
由于flag 变量为 volatile 变量,那么在进行指令重排序的过程的时候,不会将语句 3 放到语句 1、
语句 2 前面,也不会将语句 3 放到语句 4、语句 5 后面。但是要注意语句 1 和语句 2 的顺序、语
句 4 和语句 5 的顺序是不做任何保证的。
Thread中start()和run()区别
start()方法被用来启动新创建的线程,而且 start()内部调用了 run()方法,这和直接调用 run()方法的 效果不一样。当你调用 run()方法的时候,只会是在原来的线程中调用,没有新的线程启动,start() 方法才会启动新线程。
Java对象创建过程
- 类加载器将类加载到内存里
- 对类的静态变量、成员变量、静态代码块进行初始化
- 根据类信息得知目标对象大小,在堆内存里分配空间
- 内存分配的方式一般有两种,一种指针碰撞,另一种是空闲列表,JVM 会根据 Java 堆内存是否规整来决定内存分配方式
- 目标对象里的普通成员变量初始化为零值,int为0,对象为null
- 设置对象头:如类元信息、GC分代年龄、hashcode、锁标记等
- 执行目标对象内部生成的 init 方法
- init 方法是 Java 文件编译之后在字节码文件中生成的,它是一个实例构造器,这个构造器会把语句块、变量初始化、调用父类构造器等操作组织在一起。
为什么ConcurrentHashMap的key、value不允许为null,而HashMap允许?
- 防止歧义:get(key)的时候返回null,不确定是key不存在,还是value=null
- HashMap可以使用containsKey(key)来判断key是否存在,ConcurrentHashMap使用containsKey()后再使用get(key)不具有原子性,无法做出判断
ThreadLocal会出现内存泄漏吗?
- 回答:在使用不当时会出现内存泄露
Thread的成员变量ThreadLocalMap的key是指向ThreadLocal对象的弱引用,代码如下:
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
若ThreadLocal对象无其它强引用,那么可能会被GC,导致ThreadLocalMap的key为null,其对应的value就无法被访问,出现内存泄露
- 解决办法:每次使用完 ThreadLocal 以后,主动调用 remove()方法移除数据
创建线程池的办法
- 使用Executors
- newFixedThreadPool(int nThreads):创建一个固定大小的线程池。
- newCachedThreadPool():创建一个可根据需要创建新线程的线程池。
- newScheduledThreadPool(int corePoolSize):创建一个固定大小的线程池,可以执行定时任务。
- newSingleThreadExecutor():创建一个单线程的线程池。
- 使用 ThreadPoolExecutor 类的构造方法创建自定义线程池