大数据技术之_30_JVM学习_01_JVM 位置 + JVM 体系结构概览 + 堆体系结构概述 + 堆参数调优入门 + JVM 的配置和优化 + Tomcat 的配置和优化

本文主要是介绍大数据技术之_30_JVM学习_01_JVM 位置 + JVM 体系结构概览 + 堆体系结构概述 + 堆参数调优入门 + JVM 的配置和优化 + Tomcat 的配置和优化,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

大数据技术之_30_JVM学习_01

      • 1、JVM 位置
      • 2、JVM 体系结构概览
      • 3、堆体系结构概述
      • 4、堆参数调优入门
      • 5、JVM 的配置和优化
      • 6、Tomcat 的配置和优化

熟悉 JVM 架构与 GC 垃圾回收机制以及相应的 JVM 调优,有过在 Linux 系统下的调优经验。

淘宝的周志明《深入理解 Java 虚拟机》中说 JVM 的优化,其中 99% 优化的是堆,1% 优化的是方法区。

内地女歌手照片–李嘉欣,贴在桌面上。

1、JVM 位置

JVM 是运行在操作系统之上的,它与硬件没有直接的交互

2、JVM 体系结构概览

详解如下:

类装载器 Class Loader
  负责加载 class 文件,class 文件在文件开头有特定的文件标示,并且 Class Loader 只负责 class 文件的加载,至于它是否可以运行,则由 Execution Engine 决定。

虚拟机自带的加载器
  启动类加载器(Bootstrap)C++
  扩展类加载器(Extension)Java
  应用程序类加载器(AppClassLoader)Java,也叫系统类加载器,加载当前应用的 classpath 的所有类

用户自定义的加载器
  Java.lang.ClassLoader 的子类,用户可以定制类的加载方式。
  Code 案例
  sun.misc.Launcher 它是一个 java 虚拟机的入口应用。

Execution Engine 执行引擎
  执行引擎负责解释命令,提交操作系统执行。

Native Interface 本地接口
  本地接口的作用是融合不同的编程语言为 Java 所用,它的初衷是融合 C/C++ 程序,Java 诞生的时候是 C/C++ 横行的时候,要想立足,必须有调用 C/C++ 程序,于是就在内存中专门开辟了一块区域处理标记为 native 的代码,它的具体做法是 Native Method Stack 中登记 native 方法,在 Execution Engine 执行时加载 native libraies。

  目前该方法使用的越来越少了,除非是与硬件有关的应用,比如通过 Java 程序驱动打印机或者 Java 系统管理生产设备,在企业级应用中已经比较少见。

  因为现在的异构领域间的通信很发达,比如可以使用 Socket 通信,也可以使用 Web Service 等等,不多做介绍。

Native Method Stack 本地方法栈
  它的具体做法是 Native Method Stack 中登记 native 方法,在 Execution Engine 执行时加载本地方法库。

PC Register 程序寄存器
  每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向下一条指令的地址,也即将要执行的指令代码),由执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不记。

  这块内存区域很小,它是当前线程所执行的字节码的行号指示器,字节码解释器通过改变这个计数器的值来选取下一条需要执行的字节码指令。

  如果执行的是一个 Native 方法,那这个计数器是空的。

Method Area 方法区
  方法区是被所有线程共享,所有字段和方法字节码,以及一些特殊方法如构造函数,接口代码也在此定义。简单说,所有定义的方法的信息都保存在该区域,此区属于共享区间

  静态变量 + 常量 + 类信息(构造方法/接口定义) + 运行时常量池 存在方法区中,但是实例变量存在堆内存中,和方法区无关

  方法区主要存放的是:构造方法 + 接口的代码

Stack 栈是什么
  栈也叫栈内存,主管 Java 程序的运行,是在线程创建时创建,它的生命期是跟随线程的生命期,线程结束栈内存也就释放,对于栈来说不存在垃圾回收问题,只要线程一结束该栈就 over,生命周期和线程一致,是线程私有的。8 种基本类型的变量 + 对象的引用变量 + 实例方法都是在函数的栈内存中分配。

栈存储什么?
栈帧中主要保存 3 类数据:
  本地变量(Local Variables):输入参数和输出参数以及方法内的变量。
  栈操作(Operand Stack):记录出栈、入栈的操作。
  栈帧数据(Frame Data):包括类文件、方法等等。

栈运行原理
  栈中的数据都是以栈帧(Stack Frame)的格式存在,栈帧是一个内存区块,是一个数据集,是一个有关方法(Method)和运行期数据的数据集,当一个方法 A 被调用时就产生了一个栈帧 F1,并被压入到栈中,A 方法又调用了 B 方法,于是产生栈帧 F2 也被压入栈,B 方法又调用了 C 方法,于是产生栈帧 F3 也被压入栈,… 执行完毕后,先弹出 F3 栈帧,再弹出 F2 栈帧,再弹出 F1 栈帧,…

  遵循 “先进后出”/“后进先出” 原则。

  每个方法执行的同时都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息,每一个方法从调用直至执行完毕的过程,就对应着一个栈帧在虚拟机中入栈到出栈的过程。栈的大小和具体 JVM 的实现有关,通常在 256K~756K 之间。

  栈内存溢出异常:Exception in thread “main” java.lang.StackOverflowError

  栈管运行,堆管存储。

三种 JVM
  1、Sun 公司的 HotSpot(Oracle 收购)
  2、BEA 公司的 JRockit(Oracle 收购)
  3、IBM 公司的 J9 VM

3、堆体系结构概述

JVM 优化的是哪里?

Heap 堆
  一个 JVM 实例只存在一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行,堆内存分为三部分:

  • Young Generation Space 新生区 Young/New
  • Tenure generation space 养老区 Old/Tenure
  • Permanent Space 永久区 Perm

Heap 堆(Java7 之前)
  一个 JVM 实例只存在一个堆内存,堆内存的大小是可以调节的。类加载器读取了类文件后,需要把类、方法、常变量放到堆内存中,保存所有引用类型的真实信息,以方便执行器执行。
  堆内存逻辑上分为三部分:新生 + 养老 + 永久

新生区
  新生区是类的诞生、成长、消亡的区域,一个类在这里产生、应用、最后被垃圾回收器收集、结束生命。新生区又分为两部分:伊甸区(Eden space)和幸存者区(Survivor pace),所有的类都是在伊甸区被 new 出来的。幸存区有两个:0 区(Survivor 0 space)和 1 区(Survivor 1 space)。当伊甸园的空间用完时,程序又需要创建对象,JVM 的垃圾回收器将对伊甸园区进行垃圾回收
(Minor GC),将伊甸园区中的不再被其他对象所引用的对象进行销毁。然后将伊甸园中的剩余对象移动到幸存 0 区。若幸存 0 区也满了,再对该区进行垃圾回收,然后移动到 1 区。那如果
1 区也满了呢?再移动到养老区。若养老区也满了,那么这个时候将产生 Major GCFull GC),进行养老区的内存清理。若养老区执行了 Full GC 之后发现依然无法进行对象的保存,就会产生 OOM 异常 “OutOfMemoryError”。

  如果出现 java.lang.OutOfMemoryError: Java heap space 异常,说明 Java 虚拟机的堆内存不够。原因有二:
  (1)Java 虚拟机的堆内存设置不够,可以通过参数 -Xms、-Xmx 来调整
  (2)代码中创建了大量大对象,并且长时间不能被垃圾收集器收集(存在被引用)
  如何 new 一个大对象? 答:byte[] byteArray = new byte[1 * 1024 * 1024 * 7000];

养老区
  养老区用于保存从新生区筛选出来的 Java 对象,一般池对象都在这个区域活跃。

永久区
  永久存储区是一个常驻内存区域,用于存放 JDK 自身所携带的 Class、Interface 的元数据,也就是说它存储的是运行环境必须的类信息,被装载进此区域的数据是不会被垃圾回收器回收掉的,关闭 JVM 才会释放此区域所占用的内存。

  如果出现 java.lang.OutOfMemoryError: PermGen space,说明是 Java 虚拟机对永久代 Perm 内存设置不够。一般出现这种情况,都是程序启动需要加载大量的第三方 jar 包。例如:在一个 Tomcat 下部署了太多的应用。或者大量动态反射生成的类不断被加载,最终导致 Perm 区被占满。

[外链图片转存失败(img-K6IhwuSM-1563345043487)(https://s2.ax1x.com/2019/06/15/VoTYvj.png)]

  Java 7 叫永久代,Java 8 叫元空间。

  实际而言,方法区(Method Area)和堆一样,是各个线程共享的内存区域,它用于存储虚拟机加载的:类信息+普通常量+静态常量+编译器编译后的代码等等,虽然 JVM 规范将方法区描述为堆的一个逻辑部分,但它却还有一个别名叫做 Non-Heap(非堆),目的就是要和堆分开。

  对于 HotSpot 虚拟机,很多开发者习惯将方法区称之为“永久代(Parmanent Gen)”,但严格本质上说两者不同,或者说使用永久代来实现方法区而已,永久代是方法区(相当于是一个接口 interface)的一个实现jdk1.7 的版本中,已经将原本放在永久代的字符串常量池移走

jdk 1.6 方法区就是永久代。常量池在方法区中。
jdk 1.7 中 常量池放在了堆中。

[外链图片转存失败(img-ncXhpP6a-1563345043488)(https://s2.ax1x.com/2019/06/15/VoT8PS.png)]

RUNTIME CONSTANT POOL 运行时常量池
  常量池(Constant Pool)是方法区的一部分,Class 文件除了有类的版本、字段、方法、接口等描述信息外,还有一项信息就是常量池,常量池用于存放编译期间生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。

[外链图片转存失败(img-fabZBOWm-1563345043488)(https://s2.ax1x.com/2019/06/15/VoTwV0.png)]

熟悉三区结构后方可学习-JVM 垃圾收集

4、堆参数调优入门

JVM 垃圾收集(Java Garbage Collection)
以 JDK1.7 + HotSpot 为例

以 JDK1.8 + HotSpot 为例

堆内存调优简介 01

堆内存调优简介 02
发现默认的情况下分配的内存是总内存的“1 / 4”、而初始化的内存为“1 / 64”

调整 VM 参数并打印出来:-Xms1024m -Xmx1024m -XX:+PrintGCDetails

堆内存调优简介 03
java 7

java 8

堆内存调优简介 04
调整 VM 参数并打印出来:-Xms8m -Xmx8m -XX:+PrintGCDetails
java 7

java 8

MAT(Eclipse Memory Analyzer Tool)

官网访问地址:https://projects.eclipse.org/projects/tools.mat/downloads

安装方式1:离线 jar 包方式
1)Eclipse Memory Analyzer Windows 64 位下载地址:http://www.eclipse.org/downloads/download.php?file=/mat/1.8.1/rcp/MemoryAnalyzer-1.8.1.20180910-win32.win32.x86_64.zip
2)解压下载包:放到 eclipse 或 myeclipse 安装目录的 dropins 目录下。
3)启动 eclipse 或 myeclipse,打开 Window - > Perspective,看到 Memory Analyzer 证明安装成功。

安装方式2:联网插件安装方式
http://download.eclipse.org/mat/1.8.1/update-site/

使用 MAT 分析
  启动 eclipse 或 myeclipse,打开 File - > Open heap dump,在弹出的对话框选择生成的 dump文件,就可以看到 MAT 给出了overview page。

面试题
[外链图片转存失败(img-TdaUSVoc-1563345043491)(https://s2.ax1x.com/2019/06/15/VoToGD.png)]

5、JVM 的配置和优化

JVM 复习

GC 是什么
  频繁收集 新生区,使用的算法是复制算法
  较少收集 老年区,使用的算法是整理-压缩算法
  基本不动 永久区 (jdk7)

GC 三大算法

GC 算法总体概述

复制算法:Minor GC(普通 GC)
新生代中使用的是 Minor GC(普通 GC),这种 GC 算法采用的是复制算法(Copying)
复制算法的原理如下图所示:

Minor GC(普通 GC)会把 Eden 中的所有活的对象都移到 Survivor 区域中,如果 Survivor 区域中放不下,那么剩下的活的对象就被移动到 Old Generation 中,也即一旦收集后,Eden 就变成空的了
[外链图片转存失败(img-ox9vDMAT-1563345043491)(https://s2.ax1x.com/2019/06/15/Vo7Ed0.png)]

复制算法的缺点:

标记清除算法/标记整理算法:Full GC 又叫 Major GC(全局 GC)
老年代一般是由标记清除或者是标记清除与标记整理的混合实现。

标记清除:Mark-Sweep
标记清除原理

标记清除算法的缺点:

标记整理:Mark-Compact
标记整理算法原理

标记整理算法的缺点:

小总结
[外链图片转存失败(img-NMFAUa2W-1563345043492)(https://s2.ax1x.com/2019/06/15/Vo7AZq.png)]

面试题

6、Tomcat 的配置和优化


(1)点击【参数配置】选项,Eclipse 中的 Tomcat 一般在默认情况下内存偏小,运行一会儿就会抛出内存溢出错误,需要在 Tomcat 的 VM arguments 中添加如下参数:-Xms128M -Xmx512M -XX:PermSize=512m -XX:MaxPermSize=1024m,具体大小根据自己的电脑硬件。最后点击下面的 “OK” 按钮保存配置。

(2)45秒和15秒分别是tomcat启动和停止的超时时间,该长一些,防止工程较大启动慢造成工程启动不了。

(3)Eclipse默认将工程部署至eclipse的目录中,目录层次较深不易操作,这里改到tomcat自己的部署目录中。
注意:如果eclipse的tomcat已添加工程需要在eclipse中将tomcat下的工程全部移除后方可设置该项。

配置完成后,要点击【保存】按钮。

这篇关于大数据技术之_30_JVM学习_01_JVM 位置 + JVM 体系结构概览 + 堆体系结构概述 + 堆参数调优入门 + JVM 的配置和优化 + Tomcat 的配置和优化的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1100774

相关文章

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

Spring AI集成DeepSeek的详细步骤

《SpringAI集成DeepSeek的详细步骤》DeepSeek作为一款卓越的国产AI模型,越来越多的公司考虑在自己的应用中集成,对于Java应用来说,我们可以借助SpringAI集成DeepSe... 目录DeepSeek 介绍Spring AI 是什么?1、环境准备2、构建项目2.1、pom依赖2.2