The All-in-One Note (15)

如果不是用双引号声明的String对象,可以使用String提供的intern方法同步到常量池中。intern 方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池StringTable中,StringTable默认大小1009,可以通过参数修改 -XX:StringTableSize=123456...

区别

jdk6之前包括jdk6,intern方法会在常量池中创建相同String对象

jdk7开始,intern只会把堆中String对象的引用放入常量池中,主要原因是常量池从永久代已移入堆中

案例

String s = new String("1"); s.intern(); String s2 = "1"; System.out.println(s == s2); String s3 = new String("1") + new String("1"); s3.intern(); String s4 = "11"; System.out.println(s3 == s4); // 打印结果是 // jdk6 下false false // jdk7 下false true

分析:“1”会在常量池中新建一个1,然后又new了一个String对象赋值给s,调用s.intern()因为1已经在常量池中存在,所以不起效果,所以s和s1持有两个不同的引用。然后拼接两个1,创建出s3,此时常量池中不存在11,调用s3.intern()后,jdk6,会去常量池中新建一个11,而从jdk7开始,直接在常量池中创建一个保存s3地址的引用

JVM内存模型(jdk8)

线程私有

程序计数器,唯一不会OOM的区域

虚拟机栈

局部变量表

操作栈

动态链接

返回地址

本地方法栈

线程共享

Metadata元空间

本地内存存放Class对象

heap堆

常量池

实例对象

Java内存模型(JMM)

The All-in-One Note

Happens-Before原则

保证单线程执行的内存可见性,多线程不能保证

程序顺序规则:一个线程中的每个操作,happens-before于该线程中的任意后续操作

监视器锁规则:对一个锁的解锁,happens-before于随后对这个锁的加锁

volatile变量规则:对一个volatile域的写,happens-before于任意后续对这个volatile域的读

传递性:如果A happens-before B,且B happens-before C,那么A happens-before C

start()规则:如果线程A执行操作ThreadB.start()(启动线程B),那么A线程的ThreadB.start()操作happens-before于线程B中的任意操作

join()规则:如果线程A执行操作ThreadB.join()并成功返回,那么线程B中的任意操作happens-before于线程A从ThreadB.join()操作成功返回

线程中断规则:对线程interrupt方法的调用happens-before于被中断线程的代码检测到中断事件的发生

指令重排

在执行程序时,为了提高性能,编译器和处理器常常会对指令做重排序

可以通过内存屏障避免重排序(volatile的实现类似内存屏障)

如果指令执行顺序不会破坏Happens-Before原则,JVM就有可能对指令重排

类加载双亲委派机制

从底向上检查ClassLoader中类是否加载

从顶向下调用ClassLoader加载类

类加载器ClassLoader种类

BootStrapClassLoader:C++编写,加载核心库java.*

ExtClassLoader:Java编写,加载扩展库javax.*

AppClassLoader:Java编写,加载程序所在目录classpath

CustomClassLoader:Java编写,定制化加载

Java从编写到运行的大致过程

将写好的.java文件通过javac调用编译器生成JVM可识别指令组成的.class文件(IED可以自动反编译.class文件,也可以通过javap -v 反编译)

通过ClassLoader分三步加载,连接(验证,准备,解析)和初始化 将.class文件加载到JVM中生成Class类

然后用加载的Class类经过内存分配,初始化,init调用构造创建出对象

最后有了对象就可以执行相关方法了

不可变对象

对象创建之后状态不能修改

对象的所有的域都是final类型

对象是正确创建的(对象创建过程中,this引用没用逃逸)

如何安全发布对象

在静态初始化函数中初始化一个对象引用

将对象的引用保存到正确的构造对象的final类型域中

将对象的引用保存到一个由锁保护的域中

将对象的引用用volatile关键字修饰,或者保存到AtomicReference对象中

为什么双检查单例模式实例引用不加volatile不是线程安全的

对象发布主要有三步 1.分配内存空间 2初始化对象 3引用指向分配的内存

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpyysp.html