常量池
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 寄存器(程序计数器)程序计数器是一块较小的内存空间,几乎可以忽略不记,也是运行速度最快的存储区域(因为只存放了指向下一条指令的地址)。可以看作是当前线程所执行的字节码的行号指示器。
字节码解释器工 ...
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 块)中的语句合并产生的。
当初始化一个类的时候,如果发现其 ...
HashMap和Hashtable的区别
转自:HashMap 与 HashTable 的区别并加以修改
作者Hashtable:HashMap:
HashMap 比 Hashtable 多了一个并发大神:Doug Lea。
产生时间Hashtable 是 java 一开始发布时就提供的键值映射的数据结构,而 HashMap 产生于 JDK1.2。虽然 Hashtable 比 HashMap 出现的早一些,但是现在 Hashtable 基本上已经被弃用了。而 HashMap 已经成为应用最为广泛的一种数据类型了。
继承父类HashMap 是继承自AbstractMap类,而 Hashtable 是继承自Dictionary类。不过它们都实现了同时实现了map、Cloneable(可复制)、Serializable(可序列化)这三个接口
123456789public class Hashtable<K,V> extends Dictionary<K,V> implements Map<K,V>, Cloneable, java.io.Serializable{ ...
线程同步
为什么需要同步在大多数实际的多线程应用中,两个或两个以上线程需要共享对统一数据的存取。如果两个线程同时修改一个对象,可能就是导致数据不准确,使对象状态混乱,引起程序错误。因此加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用,从而保证了该变量的唯一性和准确性。
代码示例:
123456789101112131415161718192021222324252627282930313233343536373839// 模拟三个人买票,一人买10张public class BuyTicket { public static void main(String[] args) { UnsafeBuyTicket unsafeBuyTicket = new UnsafeBuyTicket(); new Thread(unsafeBuyTicket, "我").start(); new Thread(unsafeBuyTicket, "你").start(); new T ...
对象克隆
为什么要使用对象克隆首先需要说明的是,直接用=符号来复制,只是复制了原有对象的引用,对新对象的修改同样也会作用原对象上。
示例如下:
1234567891011121314151617181920212223242526class A { private String name; public A(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; }}public class CloneTest { public static void main(String[] args) { A a = new A("张三"); A b = a; ...
三种代理模式
概念代理模式(Proxy)是一种设计模式,提供了对目标对象另外的访问方式:即通过代理对象访问目标对象。这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能,例:统计,log 或对参数进行优化,更改。
这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需要修改,可以通过代理的方式来扩展该方法。
举个例子来说明代理的作用:假设我们想邀请一位明星,那么并不是直接联系明星,而是联系明星的经纪人,来达到同样的目的。明星就是一个目标对象,他只要负责活动中的节目,而其他琐碎的事情就交给他的代理人(经纪人)来解决。这就是代理思想在现实中的一个例子。
图片示例:
代理模式的关键点是:代理对象与目标对象。代理对象是对目标对象的扩展,并会调用目标对象。
代理模式的使用场景当无法或不想直接访问某个对象或访问某个对象存在困难时可以通过一个代理对象来间接访问,为了保证客户端使用的透明性,委托对象与代理对象需要实现同一个接口。
三种代理模式Java 代理分为静态代理和动态代理和 Cglib 代理。
静态代理静态代理在使用时,需要定义接口或者父类,被代理对象与代 ...