Java基础小知识点积累

对象一定分配在堆中吗?有没有了解逃逸分析技术?

  • 不一定的,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对象创建过程

  1. 类加载器将类加载到内存里
  2. 对类的静态变量、成员变量、静态代码块进行初始化
  3. 根据类信息得知目标对象大小,在堆内存里分配空间
  • 内存分配的方式一般有两种,一种指针碰撞,另一种是空闲列表,JVM 会根据 Java 堆内存是否规整来决定内存分配方式
  1. 目标对象里的普通成员变量初始化为零值,int为0,对象为null
  2. 设置对象头:如类元信息、GC分代年龄、hashcode、锁标记等
  3. 执行目标对象内部生成的 init 方法
  • init 方法是 Java 文件编译之后在字节码文件中生成的,它是一个实例构造器,这个构造器会把语句块、变量初始化、调用父类构造器等操作组织在一起。

为什么ConcurrentHashMap的key、value不允许为null,而HashMap允许?

  1. 防止歧义:get(key)的时候返回null,不确定是key不存在,还是value=null
  2. 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()方法移除数据

创建线程池的办法

  1. 使用Executors
  • newFixedThreadPool(int nThreads):创建一个固定大小的线程池。
  • newCachedThreadPool():创建一个可根据需要创建新线程的线程池。
  • newScheduledThreadPool(int corePoolSize):创建一个固定大小的线程池,可以执行定时任务。
  • newSingleThreadExecutor():创建一个单线程的线程池。
  1. 使用 ThreadPoolExecutor 类的构造方法创建自定义线程池
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容