JVM系列十四(读书笔记).

在高性能硬件上部署程序,目前主要有两种方式:

  1. 通过64位JDK来使用大内存。
  2. 通过若干个32位虚拟机建立逻辑集群来利用硬件资源。

32位的虚拟机和64位的虚拟机部署应用有什么区别?

  1. 在32位的 Windows 平台中每个进程只能使用 2GB 内存,考虑到堆以外的内存开销,堆一般最多只能开到 1.5GB;在 Linux 或者 UNIX 系统中,可以提升到 3GB 乃至接近 4GB 的内存,但仍然受到最高 4GB(2^32) 内存的限制。使用 64 位的虚拟机可以管理更大的内存。
  2. 64位虚拟机的内存回收将导致更长的时间停顿。
  3. 现阶段,64位 JDK 的性能测试结果普遍低于32位的 JDK。
  4. 64位的 JDK 要是产生堆溢出几乎就无法产生堆存储快照(因为要产生十几 GB 乃至更大的 Dump 文件),哪怕产生了快照也几乎无法进行分析。
  5. 相同程序在64位 JDK 消耗的内存一般比32位 JDK 大,这是由于指针膨胀,以及数据类型对齐补白等因素导致的。

除了Java 堆、虚拟机栈和元空间以外,下面这些区域还会占用较多的内存:

  1. Direct Memory:直接内存,多用于 NIO 操作,可通过 -XX:MaxDirectMemorySize 调整大小,内存不足时抛出 OutOfMemoryError。Direct Memory 的回收只能等待老年代满了以后 Full GC,然后“顺便地”帮它清理掉内存的废弃对象,否则它只能一直等到 OutOfMemoryError 时,先 catch 掉,再在 catch 块里面调用 System.gc()。
  2. Socket 缓冲区:每个 Socket 连接都有 Receive 和 Send 两个缓冲区,分别占大约 37KB 和 25KB 的内存,连接多的话这块内存占用也比较可观。如果无法分配,则可能会抛出 IOException:Too many open files 异常。
  3. JNI 代码:如果代码中使用 JNI 调用本地库,那本地库使用的内存也不在堆中。
  4. 虚拟机和 GC:虚拟机、GC 的代码执行也要消耗一定的内存。

破坏双亲委派模型

  1. Java 中所有涉及 SPI 的加载动作,比如 JNDI、JDBC、JCE、JAXB 和 JBI 等,都是违背了双亲委派模型来逆向使用类加载器,它利用“线程上下文类加载器”去加载所需要的 SPI 代码,也就是父类加载器请求子类加载器去完成类加载的动作。
  2. 在 OSGi 里面,加载器之间的关系不再是双亲委派模型的树形结构,而是进一步发展成了一种运行时才能确定的网状结构。OSGi 的 Bundle 类加载器之间只有规则,没有固定的委派关系。例如,某个 Bundle 声明了一个它依赖的 Package,如果有其他的 Bundle 声明发布了这个 Package 后,那么对这个 Package 的所有类加载动作都会委派给发布它的 Bundle 类加载器去完成。OSGi 也是基于此实现模块级的热插拔功能,典型的案例就是 Eclipse IDE。

字节码执行引擎

  1. 字节码执行引擎是 Java 虚拟机最核心的组成部分之一。
  2. 在 Java 虚拟机规范中制定了虚拟机字节码执行引擎的概念模型,这个概念模型成为各种虚拟机执行引擎的统一外观(Facade)。
  3. 所有 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 的每次版本升级,新增的功能大致可以分为下面四类:

  1. 在编译层面的改进。如自动装箱拆箱、变长参数、泛型等。
  2. Java API 的代码增强。如 Collections、java.util.concurrent 并发包等。
  3. 需要在字节码层面支持的改动。如升级版本号之类。
  4. 虚拟机内部的改动。这类改动对于程序员编写代码基本是透明的,但会对程序运行时产生影响。

其他

  1. 学习 JEE 规范,去看 JBoss 源码;学习类加载器,就去看 OSGi 源码。
  2. 动态代理中所谓的“动态”,是针对使用 Java 代码实际编写了代理类的 “静态”代理而言的,它的优势不仅在于省去了编写代理类那一点工作量,而是实现了可以在原始类和接口还未知的时候,就确定代理类的代理行为,当代理类与原始类脱离直接联系后,就可以很灵活地重用于不同的应用场景之中。
  3. Cpu load、jvm gc 在查看的时候,一般有个参考标准:Cpu load < 0.4C(核心数)、Full gc 发生间隔 > 6小时,耗时 < 500ms。
posted @ 2020-04-20 14:46  JMCui  阅读(...)  评论(...编辑  收藏