Integer源码解析
构造函数123456789private final int value;public Integer(int value) { this.value = value;}public Integer(String s) throws NumberFormatException { this.value = parseInt(s, 10);}
从构造方法中我们可以知道,初始化一个 Integer 对象的时候只能创建一个十进制的整数。
Integer 转 String转化为 16 进制/8 进制/2 进制方法如下:
123456789101112public static String toHexString(int i) { return toUnsignedString0(i, 4);}public static String toOctalString(int i) { return toUnsignedString0(i, 3);}public static String to ...
ArrayList源码解析
基础结构12345678910111213141516171819202122232425262728293031323334353637383940414243444546// 默认初始化容量,私有常量private static final int DEFAULT_CAPACITY = 10;// 初始容量设为0时的数组,私有常量private static final Object[] EMPTY_ELEMENTDATA = {};// 未设置初始容量时的空数组,与上一步拆分以在第一个元素添加时确定数组大小,私有常量private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};// 存放数据,如果 elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA,那么第一个数据时,容量为默认容量10transient Object[] elementData; // non-private to simplify nested cla ...
HashMap源码解析
基本结构123456789101112131415161718192021222324252627282930// 默认容量16static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16// 默认最大容量static final int MAXIMUM_CAPACITY = 1 << 30;// 默认负载因子0.75 16*0.75 = 12 即容量为13时扩容static final float DEFAULT_LOAD_FACTOR = 0.75f;// 树转化临界点8 链表容量为9时扩容static final int TREEIFY_THRESHOLD = 8;// 树退化临界点6 扩容时如果链表容量<=6,退化为链表static final int UNTREEIFY_THRESHOLD = 6;// 树容量 转换为红黑树时,如果table长度小于64,扩容static final int MIN_TREEIFY_CAPACITY = 64;// 链表节点static class N ...
常量池
Class 常量池
原文地址:Java 工程师成神之路
在 Java 中,常量池的概念想必很多人都听说过。这也是面试中比较常考的题目之一。在 Java 有关的面试题中,一般习惯通过 String 的有关问题来考察面试者对于常量池的知识的理解,几道简单的 String 面试题难倒了无数的开发者。所以说,常量池是 Java 体系中一个非常重要的概念。
谈到常量池,在 Java 体系中,共用三种常量池。分别是字符串常量池、Class 常量池和运行时常量池。
本文先来介绍一下到底什么是 Class 常量池。
什么是 Class 文件在Java 代码的编译与反编译那些事儿中我们介绍过 Java 的编译和反编译的概念。我们知道,计算机只认识 0 和 1,所以程序员写的代码都需要经过编译成 0 和 1 构成的二进制格式才能够让计算机运行。
我们在《深入分析 Java 的编译原理》中提到过,为了让 Java 语言具有良好的跨平台能力,Java 独具匠心的提供了一种可以在所有平台上都能使用的一种中间代码——字节码(ByteCode)。
有了字节码,无论是哪种平台(如 Windows、Linux 等), ...
Java内存结构
概述总所周知,Java 代码都是运行在虚拟机上的,而虚拟机在执行 Java 程序的过程中会把所管理的内存划分为若干个不同的数据区域,这些区域都有各自的用途。这些区域的统称就是运行时数据区域。也正是因为 Java 的自动内存管理机制,一旦出现内存泄漏和溢出方面的问题,如果不了解虚拟机是怎样使用内存的,那么排查错误将会是一个非常艰巨的任务。这篇文章就是介绍一下 Java 内存结构即运行时数据区域。
Java 内存结构在《Java 虚拟机规范(Java SE 8)》中描述了 JVM 运行时内存区域结构如下:
图片来自JVM 内存结构 VS Java 内存模型 VS Java 对象模型
线程共享
线程私有
Java 堆
PC 寄存器(程序计数器)
方法区
Java 虚拟机栈
本地方法栈
JDK1.8 后,方法区的实现由永久代改为了元空间,这里按下不表,后面再说。
PC 寄存器(程序计数器)程序计数器是一块较小的内存空间,几乎可以忽略不记,也是运行速度最快的存储区域(因为只存放了指向下一条指令的地址)。可以看作是当前线程所执行的字节码的行号指示器。
字节码解释器工 ...
为什么HashMap的hash值要右移16位?
前言
一时兴起,去看了看 HashMap 的源码,越过大段的注释后,第一个方法就给我难住了。为什么这么计算出的 hash 值可以更加分散呢?
1234static final int hash(Object key) { int h; return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);}
概念介绍
<<: 左移运算符,num << 1,相当于 num 乘以 2 低位补 0举例:3 << 2将数字 3 左移 2 位,将 3 转换为二进制数字:0000 0000 0000 0000 0000 0000 0000 0011,然后把该数字高位(左侧)的两个零移出,其他的数字都朝左平移 2 位,最后在低位(右侧)的两个空位补零。则得到的最终结果是0000 0000 0000 0000 0000 0000 0000 1100,则转换为十进制是 12。
>>: 右移运算符举例:11 >> 2则是将数字 11 右移 ...
Java CAS和ABA问题
本文转自Java CAS 和 ABA 问题
Java CAS 和 ABA 问题
独占锁:是一种悲观锁,synchronized 就是一种独占锁,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。
乐观锁:每次不加锁,假设没有冲突去完成某项操作,如果因为冲突失败就重试,直到成功为止。
CAS 操作乐观锁用到的机制就是 CAS,Compare and Swap。
CAS 有 3 个操作数,内存值 V,旧的预期值 A,要修改的新值 B。当且仅当预期值 A 和内存值 V 相同时,将内存值 V 修改为 B,否则什么都不做。
非阻塞算法 (nonblocking algorithms)
一个线程的失败或者挂起不应该影响其他线程的失败或挂起的算法。
现代的 CPU 提供了特殊的指令,可以自动更新共享数据,而且能够检测到其他线程的干扰,而 compareAndSet() 就用这些代替了锁定。
AtomicInteger 示例拿 AtomicInteger 来研究在没有锁的情况下是如何做到数据正确性的。
1private volatile int value;
在没有锁的机制下需要借 ...
多级缓存和一致性协议
多级缓存为什么需要 CPU cache?CPU 的频率太快了,快到主存跟不上,这样在处理器时钟周期内,CPU 常常需要等待主存,浪费资源,所以 cache 的出现,是为了缓解 CPU 和内存之间速度的不匹配问题(结构:cpu->cache->memort)
cache 工作原理cache 的工作原理是基于“局部性”原理,它包含以下两个方面:
时间局部性:如果某个数据被访问,那么在不久的将来他很可能被再次访问
空间局部性:如果某个数据被访问,那么与他相邻的数据很快也可能被访问
多级缓存是什么左图为最简单的高速缓存的配置,数据的读取和存储都经过高速缓存,CPU 核心与高速缓存有一条特殊的快速通道;主存和高速缓存都连在系统总线上,这条总线还用于其他组件的通信。高速缓存出现不久,系统变得越来越复杂,高速缓存与主存之间的速度差异被拉大,直到加入了另一级缓存,新加入的这级缓存比第一缓存更大,而且更慢,而且经济上不合适,所以有了二级缓存,甚至是三级缓存。
cache 带来的问题cache 给系统带来性能上飞跃的同时,也引入了新的问题“缓存一致性问题”。设想如下场景(cpu 一共有两 ...
线程池
本文转自java 线程池总结
线程池
池化技术的思想主要是为了减少每次获取资源的消耗,提高对资源的利用率。
线程池提供了一种限制和管理资源(包括执行一个任务)。 每个线程池还维护一些基本统计信息,例如已完成任务的数量。
这里借用《Java 并发编程的艺术》提到的来说一下使用线程池的好处:
降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。
提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
Executor 框架简介Executor 框架是 Java5 之后引进的,在 Java 5 之后,通过 Executor 来启动线程比使用 Thread 的 start 方法更好,除了更易管理,效率更好(用线程池实现,节约开销)外,还有关键的一点:有助于避免 this 逃逸问题。
补充:this 逃逸是指在构造函数返回之前其他线程就持有该对象的引用. 调用尚未构造完全的对象的方法可能引发令人疑惑的错误。
Ex ...
Java中类加载顺序
类加载机制JVM 把 class 文件加载到内存,并对数据进行校验、准备、解析、初始化,最终形成 JVM 可以直接使用的 Java 类型的过程。
图片来自【Java 基础】类加载过程
加载将 class 字节码文件加载到内存中,并将这些数据转换成方法区中的运行时数据(静态变量、静态代码块、常量池等),在堆中生成一个 Class 类对象代表这个类(反射原理),作为方法区类数据的访问入口。
链接将 Java 类的二进制代码合并到 JVM 的运行状态之中。
验证确保加载的类信息符合 JVM 规范,没有安全方面的问题。
准备正式为类变量(static 变量)分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配。注意此时的设置初始值为默认值,具体赋值在初始化阶段完成。
解析虚拟机常量池内的符号引用替换为直接引用(地址引用)的过程。
初始化初始化阶段是执行类构造器<clinit>()方法的过程。类构造器<clinit>()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static 块)中的语句合并产生的。
当初始化一个类的时候,如果发现其 ...