第九周 架构方法学习总结
JVM
JVM(java 虚拟机)就是一层用软件实现的物理机。
Java 当年主打的核心特征就是:Write Once Run Anywhere;通俗来说就是编译器将 Java 文件编译成.class
,通过 JVM 加载并执行这些.class
文件;这种执行文件放在所有平台的 JVM 上都拥有相同的产出。
JVM
JVM(java 虚拟机)就是一层用软件实现的物理机。Java 当年主打的核心特征就是:Write Once Run Anywhere;通俗来说就是编译器将 Java 文件编译成.class
,通过 JVM 加载并执行这些.class
文件;这种执行文件放在所有平台的 JVM 上都拥有相同的产出。
JVM 架构
我们先看看 JVM 的整体架构:
JVM 架构
如上图所示,JVM 主要由三部分组成:
类加载器(Class Loader Sub-system)
运行时数据区(Runtime Data Areas)
执行引擎(Execution Engine)
此外,还有两个 Native 方法相关模块:
JNI:与本机方法库进行交互,并提供执行引擎所需的本机库。
Native 方法库:执行引擎所需的本地方法库。
性能工具
JPS:用来输出 JVM 中运行的进程状态信息
Jstack:用来查看某个 Java 进程内的线程堆栈信息
Jmap & Jhat:用来查看堆内存使用状况
Jstat:JVM 统计监测工具
hprof:展现 CPU 使用率,统计堆内存使用情况
Jconsole:bin 目录下的工具,支持远程连接,可以查看 JVM 的概述,内存,线程等详细情况
Java 代码优化
线程安全
线程安全问题:在多个线程并发环境下,多个线程共同访问同一共享内存资源时,在其中一个线程对资源进行写操作的途中,其他线程对这个写了一半的资源进⾏了读操作,或者对这个写了一半的资源进⾏了写操作,最后导致此资源出现数据错误的情况。
Java 提供了一系列的关键字和类来保证线程安全:
Synchronized
:被 Synchronized 关键字描述的方法或代码块在多线程环境下同一时间只能由一个线程进行访问,保证方法或代码块操作的原子性。Volatile
:相对 Synchronized 来说 Volatile 更加轻量一些,只对基本类型的赋值操作和对象的引用赋值操作有效。java.util.concurrent.atomic
:这个包里包含了一些基于CAS(CompareAndSwap)
原理实现的类,如AtomicBoolean
、AtomicInteger
、AtomicLong
等,通过使用这些类声明可以保证对其操作具有原子性。Lock:是一系列
java.util.concurrent
包内锁的统称,主要有ReentrantLock
、ReadLock
、WriteLock
等锁类,与 Synchronized 关键字不同, Lock 提供了获取锁和释放锁等相关接口,使用上更加灵活,同时也可以做更加复杂的操作。ThreadLocal
:ThreadLocal 提供了线程的局部变量,每个线程都可以通过set()
和get()
来对这个局部变量进行操作,但不会和其他线程的局部变量进行冲突,实现了线程的数据隔离。
内存泄漏
内存泄漏:当应用程序不再使用对象时发生的情况,但是垃圾回收器无法将其从工作内存中删除,因为它们仍在被引用。因此,应用程序会消耗越来越多的资源,最终导致致命的 OutOfMemoryError。
从下图可以看到,GC 可达性标记后,所有引用被分为两种:引用和未引用。垃圾回收器只会删除未被引用的对象;有些对象出于某些原因不再被应用程序使用,但依旧被可达性标记了,这时 GC 将不会去回收这部分内存。
内存泄漏
列举几个常见的 Java 内存泄漏场景:
静态引用:方法区里的静态资源引用了一个巨大的对象
String.intern():该方法吧字符串对象存储在了 PermGen 空间中,这个空间中的对象不会被回收。不过,Java8 以后,PermGen 空间被 MetaSpace 替换了😅
在长生命周期的操作内,忘记关闭 Stream 或是未关闭连接
查找泄漏:
启用详细的垃圾回收:将 GC 参数添加到 JVM 配置中,就可以启用非常详细的 GC 跟踪
性能分析:VisualVM 等工具分析性能和调优
静态分析:定期代码审查,利用静态分析工具来帮助你了解代码和系统的状况
其他
合理使用线程池和对象池
使用合适的 JDK 容器类
缩短对象生命周期,加速垃圾回收
使用 I/O buffer 及 NIO
优先使用组合代替继承
合理使用单例模式
虚拟化所有层次
评论