<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>JVM on LeoChu Space</title>
    <link>https://leochu.work/blog/tags/jvm/</link>
    <description>Recent content in JVM on LeoChu Space</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    <lastBuildDate>Sat, 12 Apr 2025 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://leochu.work/blog/tags/jvm/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>JVM调优注意事项</title>
      <link>https://leochu.work/blog/tech/java/jvm%E8%B0%83%E4%BC%98%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9/</link>
      <pubDate>Sat, 12 Apr 2025 00:00:00 +0000</pubDate>
      <guid>https://leochu.work/blog/tech/java/jvm%E8%B0%83%E4%BC%98%E6%B3%A8%E6%84%8F%E4%BA%8B%E9%A1%B9/</guid>
      <description>&lt;p&gt;·Xmx不要超过机器内存的50%，留下些内存供VM堆外内存和操作系统使用。
·并且Xm不要超过32G。建议最大配置为30G。接近32G,JVM会启用压缩对象指针的功能，导致
性能下降。常见es集群。具体可以参考：&lt;a href=&#34;https://www.elastic.co/cn/blog/a-heap-of-trouble&#34;&gt;A Heap of Trouble: Managing Elasticsearch&amp;rsquo;s Managed Heap | Elastic Blog&lt;/a&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Java虚拟机栈</title>
      <link>https://leochu.work/blog/tech/java/java%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A0%88/</link>
      <pubDate>Sat, 15 Mar 2025 00:00:00 +0000</pubDate>
      <guid>https://leochu.work/blog/tech/java/java%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A0%88/</guid>
      <description>&lt;p&gt;与程序计数器一样，Java虚拟机栈（Java Virtual Machine Stack）也是线程私有的，它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的线程内存模型：每个方法被执行的时候，Java虚拟机都会同步创建一个栈帧（Stack Frame）用于存储&lt;a href=&#34;https://leochu.work/blog/tech/java/%E5%B1%80%E9%83%A8%E5%8F%98%E9%87%8F%E8%A1%A8/&#34;&gt;局部变量表&lt;/a&gt;、操作数栈、动态连接、方法出口等信息。每一个方法被调用直至执行完毕的过程，就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。&lt;/p&gt;
&lt;p&gt;经常有人把Java内存区域笼统地划分为堆内存（Heap）和栈内存（Stack），这种划分方式直接继承自传统的C、C++程序的内存布局结构，在Java语言里就显得有些粗糙了，实际的内存区域划分要比这更复杂。不过这种划分方式的流行也间接说明了程序员最关注的、与对象内存分配关系最密切的区域是“堆”和“栈”两块。其中，“堆”在稍后笔者会专门讲述，而“栈”通常就是指这里讲的虚拟机栈，或者更多的情况下只是指虚拟机栈中局部变量表部分。&lt;/p&gt;
&lt;p&gt;在《Java虚拟机规范》中，对这个内存区域规定了两类异常状况：如果线程请求的栈深度大于虚拟机所允许的深度，将抛出StackOverflowError异常；如果Java虚拟机栈容量可以动态扩展，当栈扩展时无法申请到足够的内存会抛出OutOfMemoryError异常。&lt;/p&gt;</description>
    </item>
    <item>
      <title>java代码的运行</title>
      <link>https://leochu.work/blog/tech/java/java%E4%BB%A3%E7%A0%81%E7%9A%84%E8%BF%90%E8%A1%8C/</link>
      <pubDate>Sat, 01 Mar 2025 00:00:00 +0000</pubDate>
      <guid>https://leochu.work/blog/tech/java/java%E4%BB%A3%E7%A0%81%E7%9A%84%E8%BF%90%E8%A1%8C/</guid>
      <description>&lt;h3 id=&#34;java-代码如何运行&#34;&gt;Java 代码如何运行&lt;/h3&gt;
&lt;p&gt;我们写的 Java 代码是高级语言，机器肯定是读不懂的。所以我们需要将它转换成机器能读懂的机器语言 (机器码)。 转换工作主要分为以下几个步骤：&lt;/p&gt;
&lt;h4 id=&#34;前端编译器&#34;&gt;前端编译器&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;javac&lt;/strong&gt; 就是前端编译器，可以将 java 文件编译成字节码组成的 class 文件。 java 代码如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Info&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;(String&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; args) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 1;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        System.&lt;span style=&#34;color:#a6e22e&#34;&gt;out&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;println&lt;/span&gt;(a);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;复制代码
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;执行 &lt;strong&gt;javac Info.java&lt;/strong&gt; 生成 Info.class 文件, 再使用 &lt;strong&gt;javap -c Info.class 来&lt;/strong&gt;查看其中的字节码。&lt;/p&gt;
&lt;p&gt;class 中字节码内容如下：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&#34;Pasted image 20230331151428.png&#34; loading=&#34;lazy&#34; src=&#34;https://leochu.work/blog/resource/Pasted%20image%2020230331151428.png&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;解释器和即时编译器&#34;&gt;解释器和即时编译器&lt;/h4&gt;
&lt;p&gt;我们通过 javac 将 java 文件编译成 class 文件，当 jvm 启动加载 class，需要逐条执行字节码指令来完成程序功能。但是程序的执行还是得在机器上，但是机器是不认识字节码的，所以我们需要将字节码转换成机器码，这样才能让机器执行程序。 &lt;em&gt;什么是机器码？&lt;/em&gt; 机器码就是用二进制代码表示的计算机能直接识别和执行的一种机器指令的集合。 而解释器和即时编译器 (Just In Time Compiler，JIT) 就是 JVM 中将字节码转化为机器码的工具。&lt;/p&gt;
&lt;h5 id=&#34;解释器&#34;&gt;解释器&lt;/h5&gt;
&lt;p&gt;解释器是一行一行地将字节码解析成机器码，解释到哪就执行到哪，狭义地说，就是 for 循环 100 次，你就要将循环体中的代码逐行解释执行 100 次。当程序需要迅速启动和执行时，解释器可以首先发挥作用，省去编译的时间，立即执行。&lt;/p&gt;</description>
    </item>
    <item>
      <title>GC Roots</title>
      <link>https://leochu.work/blog/tech/java/gc-roots/</link>
      <pubDate>Sat, 15 Feb 2025 00:00:00 +0000</pubDate>
      <guid>https://leochu.work/blog/tech/java/gc-roots/</guid>
      <description>&lt;p&gt;·在虚拟机栈（栈帧中的本地变量表）中引用的对象，譬如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等。&lt;/p&gt;
&lt;p&gt;·在方法区中类静态属性引用的对象，譬如Java类的引用类型静态变量。&lt;/p&gt;
&lt;p&gt;·在方法区中常量引用的对象，譬如字符串常量池（String Table）里的引用。&lt;/p&gt;
&lt;p&gt;·在本地方法栈中JNI（即通常所说的Native方法）引用的对象。&lt;/p&gt;
&lt;p&gt;·Java虚拟机内部的引用，如基本数据类型对应的Class对象，一些常驻的异常对象（比如NullPointExcepiton、OutOfMemoryError）等，还有系统类加载器。&lt;/p&gt;
&lt;p&gt;·所有被同步锁（synchronized关键字）持有的对象。&lt;/p&gt;
&lt;p&gt;·反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。&lt;/p&gt;
&lt;p&gt;除了这些固定的GC Roots集合以外，根据用户所选用的垃圾收集器以及当前回收的内存区域不同，还可以有其他对象“临时性”地加入，共同构成完整GC Roots集合。譬如后文将会提到的分代收集和局部回收（Partial GC），如果只针对Java堆中某一块区域发起垃圾收集时（如最典型的只针对新生代的垃圾收集），必须考虑到内存区域是虚拟机自己的实现细节（在用户视角里任何内存区域都是不可见的），更不是孤立封闭的，所以某个区域里的对象完全有可能被位于堆中其他区域的对象所引用，这时候就需要将这些关联区域的对象也一并加入GC Roots集合中去，才能保证可达性分析的正确性。&lt;/p&gt;
&lt;p&gt;目前最新的几款垃圾收集器 无一例外都具备了局部回收的特征，为了避免GC Roots包含过多对象而过度膨胀，它们在实现上也做出了各种优化处理。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Exact VM</title>
      <link>https://leochu.work/blog/tech/java/exact-vm/</link>
      <pubDate>Sat, 01 Feb 2025 00:00:00 +0000</pubDate>
      <guid>https://leochu.work/blog/tech/java/exact-vm/</guid>
      <description>&lt;p&gt;Exact VM因它使用准确式内存管理（Exact Memory Management，也可以叫Non-Con-servative/Accurate Memory Management）而得名。&lt;/p&gt;
&lt;p&gt;准确式内存管理是指虚拟机可以知道内存中某个位置的数据具体是什么类型。&lt;/p&gt;
&lt;p&gt;譬如内存中有一个32bit的整数123456，虚拟机将有能力分辨出它到底是一个指向了123456的内存地址的引用类型还是一个数值为123456的整数，准确分辨出哪些内存是引用类型，这也是在垃圾收集时准确判断堆上的数据是否还可能被使用的前提。&lt;/p&gt;
&lt;p&gt;由于使用了准确式内存管理，Exact VM可以抛弃掉以前Classic VM基于句柄（Handle）的对象查找方式（原因是垃圾收集后对象将可能会被移动位置，如果地址为123456的对象移动到654321，在没有明确信息表明内存中哪些数据是引用类型的前提下，那虚拟机肯定是不敢把内存中所有为123456的值改成654321的，所以要使用句柄来保持引用值的稳定），这样每次定位对象都少了一次间接查找的开销，显著提升执行性能。&lt;/p&gt;</description>
    </item>
    <item>
      <title>深入理解jvm</title>
      <link>https://leochu.work/blog/reading/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3jvm/</link>
      <pubDate>Sat, 04 Jan 2025 00:00:00 +0000</pubDate>
      <guid>https://leochu.work/blog/reading/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3jvm/</guid>
      <description>&lt;h2 id=&#34;第一部分-走进java&#34;&gt;第一部分 走进java&lt;/h2&gt;
&lt;h3 id=&#34;第一章-走进java&#34;&gt;第一章 走进java&lt;/h3&gt;
&lt;h4 id=&#34;12-java技术体系&#34;&gt;1.2 java技术体系&lt;/h4&gt;
&lt;p&gt;&lt;img alt=&#34;Pasted image 20230331145417.png&#34; loading=&#34;lazy&#34; src=&#34;https://leochu.work/blog/resource/Pasted%20image%2020230331145417.png&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;13-java发展史&#34;&gt;1.3 java发展史&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Jigsaw项目：虚拟机层面的模块化支持。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;DLL HELL :  &lt;a href=&#34;https://leochu.work/blog/tech/engineering/%E6%A8%A1%E5%9D%97%E5%9C%B0%E7%8B%B1/&#34;&gt;模块地狱&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id=&#34;14-jvm家族&#34;&gt;1.4 jvm家族&lt;/h4&gt;
&lt;h5 id=&#34;141虚拟机始祖sun-classicexact-vm&#34;&gt;1.4.1　虚拟机始祖：Sun Classic/Exact VM&lt;/h5&gt;
&lt;p&gt;编译器/解释器/即时编译器 : &lt;a href=&#34;https://leochu.work/blog/tech/java/java%E4%BB%A3%E7%A0%81%E7%9A%84%E8%BF%90%E8%A1%8C/&#34;&gt;java代码的运行&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://leochu.work/blog/tech/java/exact-vm/&#34;&gt;Exact VM&lt;/a&gt; 使用准确式内存管理（Exact Memory Management，也可以叫Non-Con-servative/Accurate Memory Management）&lt;/p&gt;
&lt;h5 id=&#34;142武林盟主hotspot-vm&#34;&gt;1.4.2　武林盟主：HotSpot VM&lt;/h5&gt;
&lt;p&gt;HotSpot虚拟机是Sun/OracleJDK和OpenJDK中的默认Java虚拟机，也是目前使用范围最广的Java虚拟机。&lt;/p&gt;
&lt;h4 id=&#34;16实战自己编译jdk&#34;&gt;1.6　实战：自己编译JDK&lt;/h4&gt;
&lt;h5 id=&#34;161获取源码&#34;&gt;1.6.1　获取源码&lt;/h5&gt;
&lt;p&gt;&lt;a href=&#34;https://hg.openjdk.java.net/jdk/jdk12/&#34;&gt;OpenJDK 12地址&lt;/a&gt;
&lt;img alt=&#34;Pasted image 20230331162843.png&#34; loading=&#34;lazy&#34; src=&#34;https://leochu.work/blog/resource/Pasted%20image%2020230331162843.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://leochu.work/blog/tech/java/jdk8%E5%92%8Cjdk8u%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB/&#34;&gt;jdk8和jdk8u有什么区别&lt;/a&gt;?&lt;/p&gt;
&lt;h5 id=&#34;162系统需求&#34;&gt;1.6.2　系统需求&lt;/h5&gt;
&lt;p&gt;推荐linux 64位和mac os 64位&lt;/p&gt;
&lt;p&gt;查看位数命令:
&lt;code&gt;getconf LONG_BIT&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&#34;第二部分自动内存管理&#34;&gt;第二部分　自动内存管理&lt;/h2&gt;
&lt;h3 id=&#34;第2章java内存区域与内存溢出异常&#34;&gt;第2章　Java内存区域与内存溢出异常&lt;/h3&gt;
&lt;h4 id=&#34;22运行时数据区域&#34;&gt;2.2　运行时数据区域&lt;/h4&gt;
&lt;p&gt;&lt;img alt=&#34;Pasted image 20230331171948.png&#34; loading=&#34;lazy&#34; src=&#34;https://leochu.work/blog/resource/Pasted%20image%2020230331171948.png&#34;&gt;&lt;/p&gt;
&lt;h5 id=&#34;221程序计数器&#34;&gt;2.2.1　程序计数器&lt;/h5&gt;
&lt;p&gt;&lt;a href=&#34;https://leochu.work/blog/tech/java/%E7%A8%8B%E5%BA%8F%E8%AE%A1%E6%95%B0%E5%99%A8/&#34;&gt;程序计数器&lt;/a&gt;（Program Counter Register）是一块较小的内存空间，它可以看作是当前线程所执行的字节码的行号指示器。&lt;/p&gt;
&lt;h5 id=&#34;222java虚拟机栈&#34;&gt;2.2.2　Java虚拟机栈&lt;/h5&gt;
&lt;p&gt;与程序计数器一样，&lt;a href=&#34;https://leochu.work/blog/tech/java/java%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A0%88/&#34;&gt;Java虚拟机栈&lt;/a&gt;（Java Virtual Machine Stack）也是线程私有的，它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的线程内存模型：每个方法被执行的时候，Java虚拟机都会同步创建一个栈帧 用于存储局部变量表、操作数栈、动态连接、方法出口等信息。每一个方法被调用直至执行完毕的过程，就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。&lt;/p&gt;</description>
    </item>
    <item>
      <title>局部变量表</title>
      <link>https://leochu.work/blog/tech/java/%E5%B1%80%E9%83%A8%E5%8F%98%E9%87%8F%E8%A1%A8/</link>
      <pubDate>Sat, 21 Dec 2024 00:00:00 +0000</pubDate>
      <guid>https://leochu.work/blog/tech/java/%E5%B1%80%E9%83%A8%E5%8F%98%E9%87%8F%E8%A1%A8/</guid>
      <description>&lt;p&gt;局部变量表存放了编译期可知的各种Java虚拟机基本数据类型（boolean、byte、char、short、int、float、long、double）、对象引用（reference类型，它并不等同于对象本身，可能是一个指向对象起始地址的引用指针，也可能是指向一个代表对象的句柄或者其他与此对象相关的位置）和returnAddress类型（指向了一条字节码指令的地址）。&lt;/p&gt;
&lt;p&gt;这些数据类型在局部变量表中的存储空间以局部变量槽（Slot）来表示，其中64位长度的long和double类型的数据会占用两个变量槽，其余的数据类型只占用一个。局部变量表所需的内存空间在编译期间完成分配，当进入一个方法时，这个方法需要在栈帧中分配多大的局部变量空间是完全确定的，在方法运行期间不会改变局部变量表的大小。请读者注意，这里说的“大小”是指变量槽的数量，虚拟机真正使用多大的内存空间（譬如按照1个变量槽占用32个比特、64个比特，或者更多）来实现一个变量槽，这是完全由具体的虚拟机实现自行决定的事情。&lt;/p&gt;</description>
    </item>
    <item>
      <title>程序计数器</title>
      <link>https://leochu.work/blog/tech/java/%E7%A8%8B%E5%BA%8F%E8%AE%A1%E6%95%B0%E5%99%A8/</link>
      <pubDate>Sat, 07 Dec 2024 00:00:00 +0000</pubDate>
      <guid>https://leochu.work/blog/tech/java/%E7%A8%8B%E5%BA%8F%E8%AE%A1%E6%95%B0%E5%99%A8/</guid>
      <description>&lt;p&gt;程序计数器（Program Counter Register）是一块较小的内存空间，它可以看作是当前线程所执行的字节码的行号指示器。在Java虚拟机的概念模型里，字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令，它是程序控制流的指示器.&lt;/p&gt;
&lt;p&gt;由于Java虚拟机的多线程是通过线程轮流切换、分配处理器执行时间的方式来实现的，在任何一个确定的时刻，一个处理器（对于多核处理器来说是一个内核）都只会执行一条线程中的指令。因此，为了线程切换后能恢复到正确的执行位置，每条线程都需要有一个独立的程序计数器，各条线程之间计数器互不影响，独立存储，我们称这类内存区域为“线程私有”的内存。&lt;/p&gt;
&lt;p&gt;如果线程正在执行的是一个Java方法，这个计数器记录的是正在执行的虚拟机字节码指令的地址；如果正在执行的是本地（Native）方法，这个计数器值则应为空（Undefined）。此内存区域是唯一一个在《Java虚拟机规范》中没有规定任何OutOfMemoryError情况的区域。&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
