JVM系列十四(读书笔记).
程序员文章站
2022-07-09 18:50:49
在高性能硬件上部署程序,目前主要有两种方式: 1. 通过64位JDK来使用大内存。 2. 通过若干个32位虚拟机建立逻辑集群来利用硬件资源。 32位的虚拟机和64位的虚拟机部署应用有什么区别? 1. 在32位的 Windows 平台中每个进程只能使用 2GB 内存,考虑到堆以外的内存开销,堆一般最多 ......
在高性能硬件上部署程序,目前主要有两种方式:
- 通过64位jdk来使用大内存。
- 通过若干个32位虚拟机建立逻辑集群来利用硬件资源。
32位的虚拟机和64位的虚拟机部署应用有什么区别?
- 在32位的 windows 平台中每个进程只能使用 2gb 内存,考虑到堆以外的内存开销,堆一般最多只能开到 1.5gb;在 linux 或者 unix 系统中,可以提升到 3gb 乃至接近 4gb 的内存,但仍然受到最高 4gb(2^32) 内存的限制。使用 64 位的虚拟机可以管理更大的内存。
- 64位虚拟机的内存回收将导致更长的时间停顿。
- 现阶段,64位 jdk 的性能测试结果普遍低于32位的 jdk。
- 64位的 jdk 要是产生堆溢出几乎就无法产生堆存储快照(因为要产生十几 gb 乃至更大的 dump 文件),哪怕产生了快照也几乎无法进行分析。
- 相同程序在64位 jdk 消耗的内存一般比32位 jdk 大,这是由于指针膨胀,以及数据类型对齐补白等因素导致的。
除了java 堆、虚拟机栈和元空间以外,下面这些区域还会占用较多的内存:
- direct memory:直接内存,多用于 nio 操作,可通过 -xx:maxdirectmemorysize 调整大小,内存不足时抛出 outofmemoryerror。direct memory 的回收只能等待老年代满了以后 full gc,然后“顺便地”帮它清理掉内存的废弃对象,否则它只能一直等到 outofmemoryerror 时,先 catch 掉,再在 catch 块里面调用 system.gc()。
- socket 缓冲区:每个 socket 连接都有 receive 和 send 两个缓冲区,分别占大约 37kb 和 25kb 的内存,连接多的话这块内存占用也比较可观。如果无法分配,则可能会抛出 ioexception:too many open files 异常。
- jni 代码:如果代码中使用 jni 调用本地库,那本地库使用的内存也不在堆中。
- 虚拟机和 gc:虚拟机、gc 的代码执行也要消耗一定的内存。
破坏双亲委派模型
- java 中所有涉及 spi 的加载动作,比如 jndi、jdbc、jce、jaxb 和 jbi 等,都是违背了双亲委派模型来逆向使用类加载器,它利用“线程上下文类加载器”去加载所需要的 spi 代码,也就是父类加载器请求子类加载器去完成类加载的动作。
- 在 osgi 里面,加载器之间的关系不再是双亲委派模型的树形结构,而是进一步发展成了一种运行时才能确定的网状结构。osgi 的 bundle 类加载器之间只有规则,没有固定的委派关系。例如,某个 bundle 声明了一个它依赖的 package,如果有其他的 bundle 声明发布了这个 package 后,那么对这个 package 的所有类加载动作都会委派给发布它的 bundle 类加载器去完成。osgi 也是基于此实现模块级的热插拔功能,典型的案例就是 eclipse ide。
字节码执行引擎
- 字节码执行引擎是 java 虚拟机最核心的组成部分之一。
- 在 java 虚拟机规范中制定了虚拟机字节码执行引擎的概念模型,这个概念模型成为各种虚拟机执行引擎的统一外观(facade)。
- 所有 java 虚拟机的执行引擎都是一致的:输入的是字节码文件,处理过程是字节码解析的等效过程,输出的是执行结果。
tomcat 的 jsp 文件为什么可以实现热替换?
tomcat 自定义了 commonclassloader、catalinaclassloader、sharedclassloader 和 webappclassloader 四种类加载器,分别加载 /common/*、/server/*、/shared/* 和 /webapp/web-inf/* 中 java 类库的逻辑。其中 webapp 类加载器和 jsp 类加载器通常会存在多个实例,每一台 web 应用程序对应一个 webapp 类加载器,每一个 jsp 文件对应一个 jsp 类加载器,jsp 类加载器的加载范围仅仅是这个 jsp 文件所编译出来的那一个 class,它出现的目的就是为了被丢弃:当服务器检测到 jsp 文件被修改时,会替换掉目前的 jsp 类加载器的实例,并通过再建立一个新的 jsp 类加载器来实现 jsp 文件的热替换。
jdk 的每次版本升级,新增的功能大致可以分为下面四类:
- 在编译层面的改进。如自动装箱拆箱、变长参数、泛型等。
- java api 的代码增强。如 collections、java.util.concurrent 并发包等。
- 需要在字节码层面支持的改动。如升级版本号之类。
- 虚拟机内部的改动。这类改动对于程序员编写代码基本是透明的,但会对程序运行时产生影响。
其他
- 学习 jee 规范,去看 jboss 源码;学习类加载器,就去看 osgi 源码。
- 动态代理中所谓的“动态”,是针对使用 java 代码实际编写了代理类的 “静态”代理而言的,它的优势不仅在于省去了编写代理类那一点工作量,而是实现了可以在原始类和接口还未知的时候,就确定代理类的代理行为,当代理类与原始类脱离直接联系后,就可以很灵活地重用于不同的应用场景之中。