运行模式
编译执行
解释执行
混合执行
分层编译
AOT(Ahead-of-Time),字节码编译为机器代码,jaotc工具
语言特性
泛型
Lambda特性
基础类库
集合
IO/NIO
网络
并发
安全
ClassLoader(Bootstrap,Application,Extension)
类的加载过程-加载,验证,链接,初始化
SerialGC,Parallel GC,CMS,G1
编译器,运行环境,安全工具,诊断和监控工具
JVM之上的其他语言
jaotc --output libHelloWorld.so HelloWorld.class
jaotc --output libjava.base.so --module java.base
java -XX:AOTLibrary=./libHelloWorld.so,./libjava.base.so HelloWorld
Throwable和两个子类
Exception,又包括可检测(checked)异常,不检测(unchecked)异常-也就是运行时异常
Error
比如
ClassNotFoundException,运行时通过Class.forName()产生的异常
NoClassDefFoundError,JVM或ClassLoader实例尝试加载类时的异常
try-catch机制,因为创建Throwable要调用native代码fillInStacktrace,会影响性能
fianl,并发时可以省去防御代码,JVM优化时不会final没什么用
finally
finalize,这个会影响垃圾收集性能,现在可以用幻引用+引用队列的方式替代
finalize使用不当会导致死锁,挂起等,java中调用其他语言代码时,这个函数会有用
引用类型
1.强引用
2.软引用Softly Reachable,内存不足时被回收
3.弱引用Weakly Reachable,GC时被回收
4.幻引用Phantom Reachable,始终返回null,通过引用队列可以确定此对象已被回收
引用队列,被回收的对象会放入队列中
软引用的参数
-XX:SoftRefLRUPolicyMSPerMB=N
GC的诊断参数
-XX:+PrintReferenceGC 此参数在JDK9中已不存在
String,StringBuffer,StringBuilder
String的intern()放入PermGen区
-XX:StringTableSize=N
-XX:+PrintStringTableStatistics
SymbolTable statistics:
Number of buckets : 20011
Average bucket size : 1
Variance of bucket size : 1
Std. dev. of bucket size: 1
Maximum bucket size : 5
StringTable statistics:
Number of buckets : 60013
Average bucket size : 0
Variance of bucket size : 0
Std. dev. of bucket size: 0
Maximum bucket size : 3
intern()是一种显示的排重机制,JDK8之后可以自动排重
-XX:+UseStringDeduplication
字符串的一些基础操作会直接利用JVM内部的Intrinsic机制,运行的就是特殊优化的本地代码,不是Java代码生成的字节码,Intrinsic可以理解为一种利用native方式hard-code的逻辑,算是一种特别的内联,有多优化还是需要直接使用特定的CPU指定
java -XX:+PrintCompilation -XX:+UnlockDiagnosticVMOptions -XX:+PrintInlining [java类]
51 1 3 java.lang.String::hashCode (55 bytes)
52 2 3 java.lang.String::equals (81 bytes)
53 3 3 java.lang.String::charAt (29 bytes)
@ 18 java/lang/StringIndexOutOfBoundsException:: (not loaded) not inlineable
53 4 3 java.lang.String::length (6 bytes)
55 7 n 0 java.lang.System::arraycopy (native) (static)
56 5 3 java.lang.String::indexOf (70 bytes)
@ 66 java.lang.String::indexOfSupplementary (71 bytes) callee is too large
57 6 3 java.lang.Object:: (1 bytes)
57 8 3 java.lang.Math::min (11 bytes)
57 9 1 java.lang.ref.Reference::get (5 bytes)
57 10 1 java.lang.ThreadLocal::access$400 (5 bytes)
61 11 3 java.lang.AbstractStringBuilder::append (50 bytes)
@ 5 java.lang.AbstractStringBuilder::appendNull (56 bytes) callee is too large
@ 10 java.lang.String::length (6 bytes)
@ 21 java.lang.AbstractStringBuilder::ensureCapacityInternal (27 bytes)
@ 17 java.lang.AbstractStringBuilder::newCapacity (39 bytes) callee is too large
@ 20 java.util.Arrays::copyOf (19 bytes)
@ 11 java.lang.Math::min (11 bytes)
@ 14 java.lang.System::arraycopy (0 bytes) intrinsic
@ 35 java.lang.String::getChars (62 bytes) callee is too large
61 12 3 java.lang.String::getChars (62 bytes)
@ 9 java/lang/StringIndexOutOfBoundsException:: (not loaded) not inlineable
@ 27 java/lang/StringIndexOutOfBoundsException:: (not loaded) not inlineable
@ 43 java/lang/StringIndexOutOfBoundsException:: (not loaded) not inlineable
@ 58 java.lang.System::arraycopy (0 bytes) intrinsic
Java9自后,引入了Compact Strings的设计,将char[]改为byte[],加载编码的coder加速了性能
相关的Intrinsic也做了重构,但对开发者是透明的
反射
Java9做了模块化对反射做了限制,但兼容老版本
静态代理,动态代理
JDK Proxy
Spring中的cglib实现
集合类
List
Set
Map
Queue/Deuqe
相关的排序算法
Java9的 List.of() 简化创建
Hashtable,HashMap,TreeMap,LinkedHashMap
equals和hashCode
HashMap的容量(capacity)和负载系数(load factor),树化(冲突后的链表长>8)
HashMap中的hash不是用key本身的hashC偶的,而是来自于HashMap内部的另外一个hash函数
将hash的高位移到地位进行异或运算,因为有些数据计算出的哈希值差异主要在高位,而HashMap里的哈希寻址是忽略容量以上的高位,那么这种处理方式就可以有效避免类似情况下的哈希碰撞
开放定址法
基本思想是:当关键字key的哈希地址p=H(key)出现冲突时,以p为基础,产生另一个哈希地址p1,如果p1仍然冲突,再以p为基础,产生另一个哈希地址p2,…,直到找出一个不冲突的哈希地址pi ,将相应元素存入其中。
再哈希法
这种方法是同时构造多个不同的哈希函数:
Hi=RH1(key) i=1,2,…,k
当哈希地址Hi=RH1(key)发生冲突时,再计算Hi=RH2(key)……,直到冲突不再产生。这种方法不易产生聚集,但增加了计算时间。
链地址法
这种方法的基本思想是将所有哈希地址为i的元素构成一个称为同义词链的单链表,并将单链表的头指针存在哈希表的第i个单元中,因而查找、插入和删除主要在同义词链中进行。链地址法适用于经常进行插入和删除的情况。
ConcurrentHashMap
1.7和之前的实现方式
1.分成若干segment,通过hash定位到特定的segment,每个segment里面有若桶,桶里存储KV
2.put的时候通过key得到segment并加锁,然后再hash得到要添加的桶,遍历桶中链表替换节点
3.求size是分两次计算,若两次返回相同则返回,否则加锁重新计算
1.8之后的实现方式
1.不依赖segment加锁,segment数量和桶数量一致
2.对key hash计算得到存放的桶位置,为空则用CAS设置新节点
3.否则用synchronized加锁,遍历桶中数据,替换或新添加到桶中
4.判断是否需要扩容,是否需要树化
5.求size利用LogAdd累加计算
I/O
同步和异步
阻塞和非阻塞
IO不仅仅对文件,网络中也有
输入流,输出流,是用于读取/写入字节的
Reader/writer是用于操作字符,增加字符编码等功能
BufferedOutputStrea是带缓冲区的实现
很多IO类都实现了Closeable接口
每个客户端对应一个线程模式
public class DemoServer extends Thread {
private ServerSocket serverSocket;
public int getPort() {
return serverSocket.getLocalPort();
}
public void run() {
try {
serverSocket = new ServerSocket(0);
ExecutorService executor = Executors.newFixedThreadPool(8);
while (true) {
Socket socket = serverSocket.accept();
RequestHandler requestHandler = new RequestHandler(socket);
//requestHandler.start();
executor.execute(requestHandler);
}
} catch (IOException e) {
....
} finally {
.....
}
}
public static void main(String[] args) throws IOException {
DemoServer server = new DemoServer();
server.start();
try (Socket client = new Socket(InetAddress.getLocalHost(), server.getPort())) {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(client.getInputStream()));
bufferedReader.lines().forEach(s -> System.out.println(s));
}
}
}
// 简化实现,不做读取,直接发送字符串
class RequestHandler extends Thread {
private Socket socket;
RequestHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
try (PrintWriter out = new PrintWriter(socket.getOutputStream());) {
out.println("Hello world!");
out.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
}
上述代码的工作方式如下
NIO模式的代码如下
public class NIOServer extends Thread {
public void run() {
try (Selector selector = Selector.open();
ServerSocketChannel serverSocket = ServerSocketChannel.open();) {// 创建 Selector 和 Channel
serverSocket.bind(new InetSocketAddress(InetAddress.getLocalHost(), 8888));
serverSocket.configureBlocking(false);
// 注册到 Selector,并说明关注点
serverSocket.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
selector.select();// 阻塞等待就绪的 Channel,这是关键点之一
Set selectedKeys = selector.selectedKeys();
Iterator iter = selectedKeys.iterator();
while (iter.hasNext()) {
SelectionKey key = iter.next();
// 生产系统中一般会额外进行就绪状态检查
sayHelloWorld((ServerSocketChannel) key.channel());
iter.remove();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void sayHelloWorld(ServerSocketChannel server) throws IOException {
try (SocketChannel client = server.accept();) { client.write(Charset.defaultCharset().encode("Hello world!"));
}
}
// 省略了与前面类似的 main
}
上述代码的工作方式如下
NIO2 是异步模式,用完成后回调实现的
AsynchronousServerSocketChannel serverSock = AsynchronousServerSocketChannel.open().bind(sockAddr);
serverSock.accept(serverSock, new CompletionHandler<>() { // 为异步操作指定 CompletionHandler 回调函数
@Override
public void completed(AsynchronousSocketChannel sockChannel, AsynchronousServerSocketChannel serverSock) {
serverSock.accept(serverSock, this);
// 另外一个 write(sock,CompletionHandler{})
sayHelloWorld(sockChannel, Charset.defaultCharset().encode
("Hello World!"));
}
// 省略其他路径处理方法...
});
文件拷贝
普通方式
NIO transferTo/From方式
Files工具类
拷贝实现机制,用户态->内核态
transferTo直接在内核态拷贝传输
Files工具类实现的拷贝,不同平台有不同的实现方式
其实只是用户态拷贝
NIO Buffer
capcity,反映这个Buffer到底多大,也就是数组程度
position,要操作的数据起始位置
limit, 相当于操作的限额,在读取或写入时,limit设置到所容纳的数据上线,写入时设置为容量的下限
mark, 记录上一次positioni的位置,默认是0
DirectBuffer
MappedByteBuffer,讲啊文件按照指定大小直接映射为内存区域
设置DirectBuffer参数
-XX:MaxDirectMemorySize=512M
DirectBuffer垃圾收集基于Cleaner和幻象引用
对irect Buffer 的回收的建议:
在应用程序中,显式地调用 System.gc() 来强制触发
另外一种思路是,在大量使用 Direct Buffer 的部分框架中,框架会自己在程序中调用释放方法,Netty 就是这么做的,有兴趣可以参考其实现(PlatformDependent0)。
重复使用 Direct Buffer
在 JDK 8 之后的版本,可以用 Native Memory Tracking(NMT)特性来进行DirectBuffer诊断,
注意,激活 NMT 通常都会导致 JVM 出现 5%~10% 的性能下降
-XX:NativeMemoryTracking={summary|detail}
注意,激活 NMT 通常都会导致 JVM 出现 5%~10% 的性能下降,请谨慎考虑。
运行时,可以采用下面命令进行交互式对比
// 打印 NMT 信息
jcmd VM.native_memory detail
// 进行 baseline,以对比分配内存变化
jcmd VM.native_memory baseline
// 进行 baseline,以对比分配内存变化
jcmd VM.native_memory detail.diff
//JDK 9 的输出片段如下,“+”表示的就是 diff 命令发现的分配变化:
-Internal (reserved=679KB +4KB, committed=679KB +4KB)
(malloc=615KB +4KB #1571 +4)
(mmap: reserved=64KB, committed=64KB)
设计模式可以分为创建型模式、结构型模式和行为型模式
创建型模式,是对对象创建过程的各种问题和解决方案的总结,包括
结构型模式,是针对软件设计结构的总结,关注于类、对象继承、组合方式的实践经验。
行为型模式,是从类或对象之间交互、职责划分等角度总结的模式
参考
Java技术36讲-笔记