Files
www/public/blogs/java/jvm/JVM-内存结构.html
zhuhjay 22e48d9558 build(www): 添加 Drone CI 流水线配置
- 新增 .drone.yml 文件用于定义 CI/CD 流程
- 配置了基于 Docker 的部署步骤
- 设置了工作区和卷映射以支持持久化数据
- 添加了构建准备阶段和 Docker 部署阶段
- 定义了环境变量和代理设置
- 配置了 artifacts 目录的处理逻辑
- 添加了 timezone 映射以确保时间同步
- 设置了 docker.sock 映射以支持 Docker in Docker
2025-11-01 13:36:00 +08:00

600 lines
141 KiB
HTML
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>JVM之内存结构 | ZhuHJay Blog</title>
<meta name="generator" content="VuePress 1.9.7">
<link rel="icon" href="/favicon.ico">
<meta name="description" content="my blog">
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no">
<link rel="preload" href="/assets/css/0.styles.93f9cd2e.css" as="style"><link rel="preload" href="/assets/js/app.049e1b5b.js" as="script"><link rel="preload" href="/assets/js/3.ff0e945d.js" as="script"><link rel="preload" href="/assets/js/1.1ced4111.js" as="script"><link rel="preload" href="/assets/js/18.a55369bc.js" as="script"><link rel="prefetch" href="/assets/js/10.21e2f029.js"><link rel="prefetch" href="/assets/js/11.18770288.js"><link rel="prefetch" href="/assets/js/12.588a1dd8.js"><link rel="prefetch" href="/assets/js/13.e66c15d0.js"><link rel="prefetch" href="/assets/js/14.141a3334.js"><link rel="prefetch" href="/assets/js/15.bd05cf61.js"><link rel="prefetch" href="/assets/js/16.346083d6.js"><link rel="prefetch" href="/assets/js/17.382f52f1.js"><link rel="prefetch" href="/assets/js/19.a2a264ee.js"><link rel="prefetch" href="/assets/js/20.c6871f96.js"><link rel="prefetch" href="/assets/js/21.2956f057.js"><link rel="prefetch" href="/assets/js/4.581595e5.js"><link rel="prefetch" href="/assets/js/5.55d6cfd7.js"><link rel="prefetch" href="/assets/js/6.9619dc5e.js"><link rel="prefetch" href="/assets/js/7.d2b7a379.js"><link rel="prefetch" href="/assets/js/8.9f502ca9.js"><link rel="prefetch" href="/assets/js/9.ec0bd674.js">
<link rel="stylesheet" href="/assets/css/0.styles.93f9cd2e.css">
</head>
<body>
<div id="app" data-server-rendered="true"><div class="theme-container" data-v-7dd95ae2><div data-v-7dd95ae2><div class="password-shadow password-wrapper-out" style="display:none;" data-v-59e6cb88 data-v-7dd95ae2 data-v-7dd95ae2><h3 class="title" data-v-59e6cb88>ZhuHJay Blog</h3> <p class="description" data-v-59e6cb88>my blog</p> <label id="box" class="inputBox" data-v-59e6cb88><input type="password" value="" data-v-59e6cb88> <span data-v-59e6cb88>Konck! Knock!</span> <button data-v-59e6cb88>OK</button></label> <div class="footer" data-v-59e6cb88><span data-v-59e6cb88><i class="iconfont reco-theme" data-v-59e6cb88></i> <a target="blank" href="https://vuepress-theme-reco.recoluan.com" data-v-59e6cb88>vuePress-theme-reco</a></span> <span data-v-59e6cb88><i class="iconfont reco-copyright" data-v-59e6cb88></i> <a data-v-59e6cb88><span data-v-59e6cb88>ZhuHJay</span>
  
<span data-v-59e6cb88>2021 - </span>
2025
</a></span></div></div> <div class="hide" data-v-7dd95ae2><header class="navbar" data-v-7dd95ae2><div class="sidebar-button"><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512" class="icon"><path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z"></path></svg></div> <a href="/" class="home-link router-link-active"><img src="/logo.png" alt="ZhuHJay Blog" class="logo"> <span class="site-name">ZhuHJay Blog</span></a> <div class="links"><div class="color-picker"><a class="color-button"><i class="iconfont reco-color"></i></a> <div class="color-picker-menu" style="display:none;"><div class="mode-options"><h4 class="title">Choose mode</h4> <ul class="color-mode-options"><li class="dark">dark</li><li class="auto active">auto</li><li class="light">light</li></ul></div></div></div> <div class="search-box"><i class="iconfont reco-search"></i> <input aria-label="Search" autocomplete="off" spellcheck="false" value=""> <!----></div> <nav class="nav-links can-hide"><div class="nav-item"><a href="/" class="nav-link"><i class="iconfont reco-home"></i>
首页
</a></div><div class="nav-item"><div class="dropdown-wrapper"><a class="dropdown-title"><span class="title"><i class="iconfont reco-category"></i>
分类
</span> <span class="arrow right"></span></a> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/categories/Java/" class="nav-link"><i class="undefined"></i>
Java
</a></li><li class="dropdown-item"><!----> <a href="/categories/NoSQL/" class="nav-link"><i class="undefined"></i>
NoSQL
</a></li><li class="dropdown-item"><!----> <a href="/categories/JavaScript/" class="nav-link"><i class="undefined"></i>
JavaScript
</a></li></ul></div></div><div class="nav-item"><a href="/tag/" class="nav-link"><i class="iconfont reco-tag"></i>
标签
</a></div><div class="nav-item"><a href="https://gitee.com/ZhuHJay" target="_blank" rel="noopener noreferrer" class="nav-link external"><i class="iconfont reco-mayun"></i>
码云
<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div> <!----></nav></div></header> <div class="sidebar-mask" data-v-7dd95ae2></div> <aside class="sidebar" data-v-7dd95ae2><div class="personal-info-wrapper" data-v-1fad0c41 data-v-7dd95ae2><img src="/avatar.png" alt="author-avatar" class="personal-img" data-v-1fad0c41> <h3 class="name" data-v-1fad0c41>
ZhuHJay
</h3> <div class="num" data-v-1fad0c41><div data-v-1fad0c41><h3 data-v-1fad0c41>11</h3> <h6 data-v-1fad0c41>文章</h6></div> <div data-v-1fad0c41><h3 data-v-1fad0c41>5</h3> <h6 data-v-1fad0c41>标签</h6></div></div> <ul class="social-links" data-v-1fad0c41></ul> <hr data-v-1fad0c41></div> <nav class="nav-links"><div class="nav-item"><a href="/" class="nav-link"><i class="iconfont reco-home"></i>
首页
</a></div><div class="nav-item"><div class="dropdown-wrapper"><a class="dropdown-title"><span class="title"><i class="iconfont reco-category"></i>
分类
</span> <span class="arrow right"></span></a> <ul class="nav-dropdown" style="display:none;"><li class="dropdown-item"><!----> <a href="/categories/Java/" class="nav-link"><i class="undefined"></i>
Java
</a></li><li class="dropdown-item"><!----> <a href="/categories/NoSQL/" class="nav-link"><i class="undefined"></i>
NoSQL
</a></li><li class="dropdown-item"><!----> <a href="/categories/JavaScript/" class="nav-link"><i class="undefined"></i>
JavaScript
</a></li></ul></div></div><div class="nav-item"><a href="/tag/" class="nav-link"><i class="iconfont reco-tag"></i>
标签
</a></div><div class="nav-item"><a href="https://gitee.com/ZhuHJay" target="_blank" rel="noopener noreferrer" class="nav-link external"><i class="iconfont reco-mayun"></i>
码云
<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></div> <!----></nav> <ul class="sidebar-links"><li><a href="/blogs/java/jvm/JVM-内存结构.html" class="active sidebar-link">JVM之内存结构</a></li><li><a href="/blogs/java/jvm/JVM-垃圾回收.html" class="sidebar-link">JVM之垃圾回收</a></li><li><a href="/blogs/java/jvm/JVM-字节码技术.html" class="sidebar-link">JVM之字节码技术</a></li><li><a href="/blogs/java/jvm/JVM-类加载.html" class="sidebar-link">JVM之类加载</a></li></ul> </aside> <div class="password-shadow password-wrapper-in" style="display:none;" data-v-59e6cb88 data-v-7dd95ae2><h3 class="title" data-v-59e6cb88>JVM之内存结构</h3> <!----> <label id="box" class="inputBox" data-v-59e6cb88><input type="password" value="" data-v-59e6cb88> <span data-v-59e6cb88>Konck! Knock!</span> <button data-v-59e6cb88>OK</button></label> <div class="footer" data-v-59e6cb88><span data-v-59e6cb88><i class="iconfont reco-theme" data-v-59e6cb88></i> <a target="blank" href="https://vuepress-theme-reco.recoluan.com" data-v-59e6cb88>vuePress-theme-reco</a></span> <span data-v-59e6cb88><i class="iconfont reco-copyright" data-v-59e6cb88></i> <a data-v-59e6cb88><span data-v-59e6cb88>ZhuHJay</span>
  
<span data-v-59e6cb88>2021 - </span>
2025
</a></span></div></div> <div data-v-7dd95ae2><div data-v-7dd95ae2><main class="page"><section style="display:;"><div class="page-title"><h1 class="title">JVM之内存结构</h1> <div data-v-8a445198><i class="iconfont reco-account" data-v-8a445198><span data-v-8a445198>ZhuHJay</span></i> <i class="iconfont reco-date" data-v-8a445198><span data-v-8a445198>2022/11/22</span></i> <!----> <i class="tags iconfont reco-tag" data-v-8a445198><span class="tag-item" data-v-8a445198>JVM</span></i></div></div> <div class="theme-reco-content content__default"><h2 id="_1-程序计数器"><a href="#_1-程序计数器" class="header-anchor">#</a> 1 程序计数器</h2> <p><img src="/jvm/1669015637679.png" alt="1669015637679"></p> <p>Program Counter Register 程序计数器(寄存器)</p> <ul><li>作用是记住下一条jvm指令的执行地址</li> <li>特点
<ul><li>是线程私有的</li> <li>不会存在内存溢出</li></ul></li></ul> <h2 id="_2-虚拟机栈"><a href="#_2-虚拟机栈" class="header-anchor">#</a> 2 虚拟机栈</h2> <p><img src="/jvm/1669015814320.png" alt="1669015814320"></p> <h3 id="_2-1-定义"><a href="#_2-1-定义" class="header-anchor">#</a> 2.1 定义</h3> <p>Java Virtual Machine Stacks Java 虚拟机栈)</p> <ul><li>每个线程运行时所需要的内存,称为虚拟机栈</li> <li>每个栈由多个栈帧Frame组成对应着每次方法调用时所占用的内存</li> <li>每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法</li></ul> <p>问题辨析</p> <ol><li>垃圾回收是否涉及栈内存?
<ul><li>每一个栈帧使用完后会自动释放该栈帧所对应的内存,垃圾回收只涉及到堆内存</li></ul></li> <li>栈内存分配越大越好吗?
<ul><li>每个线程都会对应一个栈内存,如果栈内存分配越大则所能使用的线程数则减小,也对程序的执行起不到加快的作用</li></ul></li> <li>方法内的局部变量是否线程安全?
<ul><li>如果方法内局部变量没有逃离方法的作用访问,它是线程安全的</li> <li>如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全</li></ul></li></ol> <h3 id="_2-2-栈内存溢出"><a href="#_2-2-栈内存溢出" class="header-anchor">#</a> 2.2 栈内存溢出</h3> <ul><li><p>栈帧过多导致栈内存溢出</p> <ul><li>递归没有给到合适的出口</li> <li>第三方组件使用不当导致循环引用ObjectMapper对象 -&gt; Json</li></ul></li> <li><p>栈帧过大导致栈内存溢出(局部变量过多导致,但较难触发)</p></li></ul> <blockquote><p>Tip</p> <ul><li><p>可以使用 <code>-Xss</code> 来改变栈内存的大小</p></li> <li><p>栈内存溢出的错误信息为:<code>java.lang.StackOverflowError</code></p></li></ul></blockquote> <h3 id="_2-3-线程运行诊断"><a href="#_2-3-线程运行诊断" class="header-anchor">#</a> 2.3 线程运行诊断</h3> <p>eg1CPU占用过多</p> <p>定位问题步骤Linux</p> <ul><li><code>top</code> 命令定位哪个进程对CPU的占用过高</li> <li><code>ps H -eo pid,tid,%cpu | grep 进程id</code> (用<code>ps</code>命令进一步定义是哪个线程引起的cpu占用过高</li> <li><code>jstack 进程id</code> 可以根据线程id找到有问题的线程进一步定位到问题代码的源码行号</li></ul> <p>eg2程序运行很长时间没有结果线程死锁</p> <h2 id="_3-本地方法栈"><a href="#_3-本地方法栈" class="header-anchor">#</a> 3 本地方法栈</h2> <p><img src="/jvm/1669019510517.png" alt="1669019510517"></p> <p>本地方法栈的作用类似于JVM虚拟机栈就是调用本地方法Native时会开辟一个本地方法的执行内存也就是本地方法栈其中有调用库函数的执行栈帧。</p> <h2 id="_4-堆"><a href="#_4-堆" class="header-anchor">#</a> 4 堆</h2> <p><img src="/jvm/1669021362263.png" alt="1669021362263"></p> <h3 id="_4-1-定义"><a href="#_4-1-定义" class="header-anchor">#</a> 4.1 定义</h3> <p>Heap 堆</p> <ul><li>通过 new 关键字,创建对象都会使用堆内存</li></ul> <p>特点</p> <ul><li>它是线程共享的,堆中对象都需要考虑线程安全的问题</li> <li>有垃圾回收机制</li></ul> <h3 id="_4-2-堆内存溢出"><a href="#_4-2-堆内存溢出" class="header-anchor">#</a> 4.2 堆内存溢出</h3> <p>当创建的对象足够多并且这些对象都正在被使用,不会被当成垃圾进行处理时,就会产生堆内存溢出的现象</p> <blockquote><p>Tip</p> <ul><li>可以使用 <code>-Xmx</code> 来改变堆内存的最大大小</li> <li>堆内存溢出的错误信息为:<code>java.lang.OutOfMemoryError: Java heap space</code></li></ul></blockquote> <h3 id="_4-3-堆内存诊断"><a href="#_4-3-堆内存诊断" class="header-anchor">#</a> 4.3 堆内存诊断</h3> <p>有如下代码进行测试</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Demo1_4</span> <span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">InterruptedException</span> <span class="token punctuation">{</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">&quot;1..&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">sleep</span><span class="token punctuation">(</span><span class="token number">30000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 分配 10M内存</span>
<span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> bytes <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token number">1024</span> <span class="token operator">*</span> <span class="token number">1024</span> <span class="token operator">*</span> <span class="token number">10</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">&quot;2..&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">sleep</span><span class="token punctuation">(</span><span class="token number">30000</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
bytes <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
<span class="token comment">// 进行一次垃圾回收</span>
<span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">gc</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">&quot;3..&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">sleep</span><span class="token punctuation">(</span><span class="token number">1000000L</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br></div></div><ol><li><p>jps 工具:查看当前系统中有哪些 Java 进程</p> <div class="language-shell line-numbers-mode"><pre class="language-shell"><code>PS JVM<span class="token operator">&gt;</span> jps
<span class="token number">12128</span> Demo1_4
<span class="token number">12116</span> RemoteMavenServer
<span class="token number">14968</span>
<span class="token number">7720</span> Jps
<span class="token number">7800</span> Launcher
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div></li> <li><p>jmap 工具:查看堆内存占用情况 <code>jmap -heap 进程id</code></p> <ul><li><p>在输出1时的堆内存情况</p> <div class="language-tex line-numbers-mode"><pre class="language-tex"><code>Heap Usage: # 堆内存占用情况
PS Young Generation
Eden Space: # 新创建对象使用的分区
capacity = 34078720 (32.5MB) # 堆内存容量
used = 4771568 (4.5505218505859375MB) # 堆内存已使用情况
free = 29307152 (27.949478149414062MB) # 可分配
14.001605694110577<span class="token comment">% used</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div></li> <li><p>在输出2时的堆内存情况因为创建了一个 10M 的字节数组,所以此时堆内存使用量比上次多了 10M 左右的使用</p> <div class="language-tex line-numbers-mode"><pre class="language-tex"><code>Heap Usage: # 堆内存占用情况
PS Young Generation
Eden Space: # 新创建对象使用的分区
capacity = 34078720 (32.5MB) # 堆内存容量
used = 15257344 (14.550537109375MB) # 堆内存已使用情况
free = 18821376 (17.949462890625MB) # 可分配
44.77088341346154<span class="token comment">% used</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div></li> <li><p>在输出3时的堆内存情况进行了垃圾回收将分配的 10M 字节数组进行回收,使得堆内存使用量大大减少</p> <div class="language-tex line-numbers-mode"><pre class="language-tex"><code>Heap Usage: # 堆内存占用情况
PS Young Generation
Eden Space: # 新创建对象使用的分区
capacity = 34078720 (32.5MB) # 堆内存容量
used = 681592 (0.6500167846679688MB) # 堆内存已使用情况
free = 33397128 (31.84998321533203MB) # 可分配
2.0000516451322117<span class="token comment">% used</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div></li></ul></li> <li><p>jconsole 工具:图形界面,多功能的监测工具,可以连续监测</p> <p>分为三个阶段的使用情况,阶段一为初始阶段。阶段二时分配了一段内存,阶段三进行了垃圾回收</p> <p><img src="/jvm/1669024208924.png" alt="1669024208924"></p></li> <li><p>jvisualvm 工具:与 jconsole 类似的图形化界面,但是该用具可以 dump堆内存快照可以查看堆中存活的对象信息</p> <ul><li><p>案例/代码:垃圾回收后,内存占用仍然很高</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Demo1_6</span> <span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">InterruptedException</span> <span class="token punctuation">{</span>
<span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">Student</span><span class="token punctuation">&gt;</span></span> list <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">200</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
list<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token keyword">new</span> <span class="token class-name">Student</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token comment">// 为了调试而休眠</span>
<span class="token class-name">Thread</span><span class="token punctuation">.</span><span class="token function">sleep</span><span class="token punctuation">(</span><span class="token number">1000000000L</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token keyword">class</span> <span class="token class-name">Student</span> <span class="token punctuation">{</span>
<span class="token keyword">private</span> <span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> big <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token number">1024</span> <span class="token operator">*</span> <span class="token number">1024</span><span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br></div></div></li> <li><p>使用 <code>jps</code> 命令获取到该程序的进程id使用 <code>jmap -heap 进程id</code> 查看堆内存使用</p> <p>可以发现堆内存总共占用了 (16+187)M然后使用 <code>jconsole</code> 工具进行该进程的 gc 回收</p> <div class="language-tex line-numbers-mode"><pre class="language-tex"><code>Heap Usage:
PS Young Generation
Eden Space:
capacity = 116916224 (111.5MB)
used = 17285928 (16.485145568847656MB)
free = 99630296 (95.01485443115234MB)
14.784883918249019<span class="token comment">% used</span>
PS Old Generation # 老年代堆内存使用情况
capacity = 334495744 (319.0MB)
used = 196736312 (187.62236785888672MB)
free = 137759432 (131.37763214111328MB)
58.815789297456654<span class="token comment">% used</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br></div></div><p>可以发现,虽然进行了 gc 垃圾回收,但是堆内存占用还是居高不下,使用 jconsole 工具已经没办法进一步查询到具体问题了</p> <p><img src="/jvm/1669043972054.png" alt="1669043972054"></p></li> <li><p>使用 jvisualvm 进行堆内存 dump</p> <p>使用命令 <code>jvisualvm</code> 打开可视化界面,选择当前执行的进程,查看详细信息,然后进行堆内存 dump</p> <p><img src="/jvm/1669044220427.png" alt="1669044220427"></p> <p>可以发现当前的 ArrayList 对象占用的内存高达约 200M 左右的内存,点击进去查看该对象的详细信息</p> <p><img src="/jvm/1669044331372.png" alt="1669044331372"></p> <p>在 ArrayList 对象中的每一个元素都占用了约 1M 的内存大小一共有200个元素则总占用就有了大约 200M 的堆内存空间,由此快速定位到代码中存在的问题</p> <p><img src="/jvm/1669044475571.png" alt="1669044475571"></p></li></ul></li></ol> <h2 id="_5-方法区"><a href="#_5-方法区" class="header-anchor">#</a> 5 方法区</h2> <p><img src="/jvm/1669044952813.png" alt="1669044952813"></p> <h3 id="_5-1-定义"><a href="#_5-1-定义" class="header-anchor">#</a> 5.1 定义</h3> <p><a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html" target="_blank" rel="noopener noreferrer">Java 虚拟机规范<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a></p> <p>以下是官方对方法区的定义</p> <blockquote><p>Java 虚拟机具有 在所有 Java 虚拟机之间共享<em>的方法区域</em> 线程。方法区域类似于已编译的存储区域传统语言的代码或类似于“文本”段的代码 操作系统进程。它存储每个类结构,如运行时常量池、字段和方法数据以及方法和构造函数,包括特殊方法 <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-2.html#jvms-2.9" target="_blank" rel="noopener noreferrer">§2.9<span><svg xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false" x="0px" y="0px" viewBox="0 0 100 100" width="15" height="15" class="icon outbound"><path fill="currentColor" d="M18.8,85.1h56l0,0c2.2,0,4-1.8,4-4v-32h-8v28h-48v-48h28v-8h-32l0,0c-2.2,0-4,1.8-4,4v56C14.8,83.3,16.6,85.1,18.8,85.1z"></path> <polygon fill="currentColor" points="45.7,48.7 51.3,54.3 77.2,28.5 77.2,37.2 85.2,37.2 85.2,14.9 62.8,14.9 62.8,22.9 71.5,22.9"></polygon></svg> <span class="sr-only">(opens new window)</span></span></a> 用于类和实例初始化 和接口初始化。</p> <p>创建方法区域 在虚拟机启动时。虽然方法区域在逻辑上是作为堆的一部分,简单的实现可以选择不垃圾收集或压缩它。这规范不要求方法区域的位置或用于管理已编译代码的策略。方法区域可以是固定大小或可根据计算需要进行扩展,并且可以如果不需要更大的方法区域,则收缩。记忆对于方法区域不需要是连续的。</p> <p>Java 虚拟机实现可以为程序员或用户控制方法区域的初始大小,以及,对于不同尺寸的方法区域,控制最大值和最小方法区域大小。</p> <p>以下特殊条件与方法区域相关联:</p> <ul><li>如果方法区域中的内存无法用于满足分配请求Java 虚拟机抛出一个。<code>OutOfMemoryError</code></li></ul></blockquote> <h3 id="_5-2-组成"><a href="#_5-2-组成" class="header-anchor">#</a> 5.2 组成</h3> <p>JDK6 和 JDK8 的内存结构发生了变化</p> <blockquote><p><strong>注意</strong>:图中的 常量池 指的是 运行时常量池</p></blockquote> <p><img src="/jvm/1669045525089.png" alt="1669045525089"></p> <h3 id="_5-3-方法区内存溢出"><a href="#_5-3-方法区内存溢出" class="header-anchor">#</a> 5.3 方法区内存溢出</h3> <ul><li>JDK1.8 以前会导致永久代内存溢出:<code>java.lang.OutOfMemoryError: PermGen space</code></li> <li>JDK1.8 以后会导致元空间内存溢出:<code>java.lang.OutOfMemoryError: Metaspace</code></li></ul> <p>演示 JDK1.8 以后元空间内存溢出JDK1.6 代码几乎相同,永久代使用 <code>-XX:MaxPermSize=8m</code></p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Demo1_7</span> <span class="token keyword">extends</span> <span class="token class-name">ClassLoader</span> <span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">int</span> j <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
<span class="token keyword">try</span> <span class="token punctuation">{</span>
<span class="token class-name">Demo1_7</span> test <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Demo1_7</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> i <span class="token operator">&lt;</span> <span class="token number">10000</span><span class="token punctuation">;</span> i<span class="token operator">++</span><span class="token punctuation">,</span> j<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token comment">// ClassWriter 作用是生成类的二进制字节码</span>
<span class="token class-name">ClassWriter</span> cw <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ClassWriter</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 版本号public类名包名父类接口</span>
cw<span class="token punctuation">.</span><span class="token function">visit</span><span class="token punctuation">(</span><span class="token class-name">Opcodes</span><span class="token punctuation">.</span><span class="token constant">V1_8</span><span class="token punctuation">,</span> <span class="token class-name">Opcodes</span><span class="token punctuation">.</span><span class="token constant">ACC_PUBLIC</span><span class="token punctuation">,</span> <span class="token string">&quot;Class&quot;</span> <span class="token operator">+</span> i<span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">,</span> <span class="token string">&quot;java/lang/Object&quot;</span><span class="token punctuation">,</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 返回byte信息</span>
<span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> code <span class="token operator">=</span> cw<span class="token punctuation">.</span><span class="token function">toByteArray</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 执行类的加载</span>
test<span class="token punctuation">.</span><span class="token function">defineClass</span><span class="token punctuation">(</span><span class="token string">&quot;Class&quot;</span> <span class="token operator">+</span> i<span class="token punctuation">,</span> code<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> code<span class="token punctuation">.</span>length<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>j<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br></div></div><p>在 1.8 以后元空间使用的是系统没存,没有限制大小,不会发生溢出的情况,需要设置 <code>-XX:MaxMetaspaceSize=8m</code></p> <div class="language-tex line-numbers-mode"><pre class="language-tex"><code>5411
Exception in thread &quot;main&quot; java.lang.OutOfMemoryError: Metaspace
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
at com.zhuhjay.Demo1_7.main(Demo1_7.java:23)
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p>方法区内存溢出场景:</p> <ul><li>Spring</li> <li>Mybatis</li></ul> <p>这些场景都会使用 CGLIB 等代理技术动态的生成字节码,使用不当可能会导致方法区内存溢出的情况。</p> <h3 id="_5-4-运行时常量池"><a href="#_5-4-运行时常量池" class="header-anchor">#</a> 5.4 运行时常量池</h3> <ul><li>常量池,就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息</li> <li>运行时常量池,常量池是 *.class 文件中的,当该类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址</li></ul> <blockquote><p><strong>Tip</strong>: 常量池的概念解析</p> <p>编译运行以下代码</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Demo1_8</span> <span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">&quot;hello world&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br></div></div><p>将编译后的 class 文件进行反编译 <code>javap -v Demo1_8.class</code></p> <ul><li>源码 1-8 行是文件的 类的基本信息</li> <li>源码 9-42 行是 常量池信息</li> <li>源码 44-72 行是 类方法定义信息</li> <li><strong>当 class 文件被运行之后,所有的 <code>#..</code> 都会被赋予真实的物理地址信息</strong></li></ul> <div class="language-tex line-numbers-mode"><pre class="language-tex"><code>Classfile /G:/Idea_workspace/JVM/target/classes/com/zhuhjay/Demo1_8.class
Last modified 2022-11-22; size 548 bytes
MD5 checksum 551e83a2ede53badac918978a8846964
Compiled from &quot;Demo1_8.java&quot;
public class com.zhuhjay.Demo1_8
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #6.#20 // java/lang/Object.&quot;&lt;init&gt;&quot;:()V
#2 = Fieldref #21.#22 // java/lang/System.out:Ljava/io/PrintStream;
#3 = String #23 // hello world
#4 = Methodref #24.#25 // java/io/PrintStream.println:(Ljava/lang/String;)V
#5 = Class #26 // com/zhuhjay/Demo1_8
#6 = Class #27 // java/lang/Object
#7 = Utf8 &lt;init&gt;
#8 = Utf8 ()V
#9 = Utf8 Code
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcom/zhuhjay/Demo1_8;
#14 = Utf8 main
#15 = Utf8 (<span class="token punctuation">[</span>Ljava/lang/String;)V
#16 = Utf8 args
#17 = Utf8 <span class="token punctuation">[</span>Ljava/lang/String;
#18 = Utf8 SourceFile
#19 = Utf8 Demo1_8.java
#20 = NameAndType #7:#8 // &quot;&lt;init&gt;&quot;:()V
#21 = Class #28 // java/lang/System
#22 = NameAndType #29:#30 // out:Ljava/io/PrintStream;
#23 = Utf8 hello world
#24 = Class #31 // java/io/PrintStream
#25 = NameAndType #32:#33 // println:(Ljava/lang/String;)V
#26 = Utf8 com/zhuhjay/Demo1_8
#27 = Utf8 java/lang/Object
#28 = Utf8 java/lang/System
#29 = Utf8 out
#30 = Utf8 Ljava/io/PrintStream;
#31 = Utf8 java/io/PrintStream
#32 = Utf8 println
#33 = Utf8 (Ljava/lang/String;)V
<span class="token punctuation">{</span>
public com.zhuhjay.Demo1_8();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object.&quot;&lt;init&gt;&quot;:()V
4: return
LineNumberTable:
line 7: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/zhuhjay/Demo1_8;
public static void main(java.lang.String<span class="token punctuation">[</span><span class="token punctuation">]</span>);
descriptor: (<span class="token punctuation">[</span>Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String hello world
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 9: 0
line 10: 8
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 args <span class="token punctuation">[</span>Ljava/lang/String;
<span class="token punctuation">}</span>
SourceFile: &quot;Demo1_8.java&quot;
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br><span class="line-number">41</span><br><span class="line-number">42</span><br><span class="line-number">43</span><br><span class="line-number">44</span><br><span class="line-number">45</span><br><span class="line-number">46</span><br><span class="line-number">47</span><br><span class="line-number">48</span><br><span class="line-number">49</span><br><span class="line-number">50</span><br><span class="line-number">51</span><br><span class="line-number">52</span><br><span class="line-number">53</span><br><span class="line-number">54</span><br><span class="line-number">55</span><br><span class="line-number">56</span><br><span class="line-number">57</span><br><span class="line-number">58</span><br><span class="line-number">59</span><br><span class="line-number">60</span><br><span class="line-number">61</span><br><span class="line-number">62</span><br><span class="line-number">63</span><br><span class="line-number">64</span><br><span class="line-number">65</span><br><span class="line-number">66</span><br><span class="line-number">67</span><br><span class="line-number">68</span><br><span class="line-number">69</span><br><span class="line-number">70</span><br><span class="line-number">71</span><br><span class="line-number">72</span><br><span class="line-number">73</span><br><span class="line-number">74</span><br></div></div><p>进行main方法信息进行分析 62-66 行代码截取在CPU中是不会读取到 <code>//</code> 后面的注释信息的,这些注释信息是为了让程序员快速明白的注释信息。</p> <div class="language-tex line-numbers-mode"><pre class="language-tex"><code>stack=2, locals=1, args_size=1
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String hello world
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br></div></div><ul><li><p><code>0: getstatic #2</code></p> <ul><li><p>getstatic获取类的成员变量</p></li> <li><p>#2去常量池 <code>Constant pool</code> 中查找对应的信息</p> <ol><li><p><code>#2 = Fieldref #21.#22</code>:需要找到地址 <code>#21.#22</code>,该组成为 成员变量的引用</p></li> <li><p><code>#21 = Class #28</code>:需要找到地址 <code>#28</code>,该地址信息为 Class</p></li> <li><p><code>#28 = Utf8 java/lang/System</code>:找到 <code>System</code> 类名</p></li> <li><p><code>#22 = NameAndType #29:#30</code>:需要找到 方法名和其类型</p></li> <li><p><code>#29 = Utf8 out</code>:找到调用的静态方法</p></li> <li><p><code>#30 = Utf8 Ljava/io/PrintStream;</code>:找到类型</p></li></ol></li> <li><p>即该条命令就是为了找到该结果 <code>Field java/lang/System.out:Ljava/io/PrintStream;</code>,这也就是源码的 <code>System.out</code></p></li></ul></li> <li><p><code>3: ldc #3</code></p> <ul><li>ldc找到一个地址并作为下一次指令的参数</li> <li>#3
<ol><li><code>#3 = String #23</code>:需要找到地址 <code>#23</code></li> <li><code>#23 = Utf8 hello world</code>:找到类型为 <code>Utf8</code> 的字符串</li></ol></li> <li>即该条指令就是为了找到结果 <code>hello world</code></li></ul></li> <li><p><code>5: invokevirtual #4</code></p> <ul><li>invokevirtual执行一次虚方法调用方法调用</li> <li>#4
<ol><li><code>#4 = Methodref #24.#25</code>:需要找到地址 <code>#24.#25</code>,该组为 方法引用</li> <li><code>#24 = Class #31</code>:需要找到地址 <code>#31</code>,是一个 Class 对象</li> <li><code>#31 = Utf8 java/io/PrintStream</code>:一个输出流的类名</li> <li><code>#25 = NameAndType #32:#33</code>:需要找到地址 <code>#32:#33</code>,方法名及其类型</li> <li><code>#32 = Utf8 println</code>:找到需要调用的方法名称</li> <li><code>#33 = Utf8 (Ljava/lang/String;)V</code>:参数类型为 String无返回值</li></ol></li> <li>即该条指令就是为了找到结果 <code>Method java/io/PrintStream.println:(Ljava/lang/String;)V</code>,也就是源码中的 <code>println(&quot;hello world&quot;)</code>,需要将上一个指令的数据作为方法参数</li></ul></li></ul></blockquote> <h3 id="_5-5-stringtable"><a href="#_5-5-stringtable" class="header-anchor">#</a> 5.5 StringTable</h3> <p>用来存储字符串的常量池,<strong>使用硬编码的字符串都会被存储到字符串常量池中</strong></p> <p>eg:</p> <ul><li><code>new String(&quot;a&quot;)</code> 会被放入字符串常量池和堆中,返回值是堆中的引用</li> <li><code>&quot;a&quot;</code> 会被直接放到字符串常量池中,返回值是串池中的引用</li></ul> <h5 id="_5-5-1-解析引入"><a href="#_5-5-1-解析引入" class="header-anchor">#</a> 5.5.1 解析引入</h5> <ul><li><p>现有以下代码,将其进行编译后,使用 <code>javap</code> 对其进行反编译,查看常量池的信息</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Demo1_9</span> <span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token class-name">String</span> s1 <span class="token operator">=</span> <span class="token string">&quot;a&quot;</span><span class="token punctuation">;</span>
<span class="token class-name">String</span> s2 <span class="token operator">=</span> <span class="token string">&quot;b&quot;</span><span class="token punctuation">;</span>
<span class="token class-name">String</span> s3 <span class="token operator">=</span> <span class="token string">&quot;ab&quot;</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div></li> <li><p>编译结果如下(只展示部分)</p> <ul><li>常量池中的信息,都会被加载到运行时常量池中,这时 a b ab 都是常量池中的符号,还没有变为 java 字符串对象</li> <li><code>ldc #2</code> 会把 a 符号变为 &quot;a&quot; 字符串对象,然后到 StringTable 中去找是否存在该值,不存在则存入 StringTable 中
<ul><li>StringTable 是 hashtable 结构,不能扩容,一创建就固定了大小</li></ul></li> <li><code>ldc #3</code> <code>ldc #4</code> 同上</li> <li><code>astore_1</code> 即是将上一条指令的内容存入到方法栈桢的变量表的 1 位置上</li></ul> <div class="language-tex line-numbers-mode"><pre class="language-tex"><code>Constant pool:
#2 = String #25 // a
#3 = String #26 // b
#4 = String #27 // ab
public static void main(java.lang.String<span class="token punctuation">[</span><span class="token punctuation">]</span>);
Code:
stack=1, locals=4, args_size=1
0: ldc #2 // String a
2: astore_1
3: ldc #3 // String b
5: astore_2
6: ldc #4 // String ab
8: astore_3
9: return
LocalVariableTable: # main方法栈帧的变量表
Start Length Slot Name Signature
0 10 0 args <span class="token punctuation">[</span>Ljava/lang/String;
3 7 1 s1 Ljava/lang/String;
6 4 2 s2 Ljava/lang/String;
9 1 3 s3 Ljava/lang/String;
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br></div></div></li></ul> <h5 id="_5-5-2-变量拼接"><a href="#_5-5-2-变量拼接" class="header-anchor">#</a> 5.5.2 变量拼接</h5> <ul><li><p>现对源代码进行改变如下,需要了解字符串变量拼接的原理,以及 <code>s3 == s4</code> 是否成立</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Demo1_9</span> <span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token class-name">String</span> s1 <span class="token operator">=</span> <span class="token string">&quot;a&quot;</span><span class="token punctuation">;</span>
<span class="token class-name">String</span> s2 <span class="token operator">=</span> <span class="token string">&quot;b&quot;</span><span class="token punctuation">;</span>
<span class="token class-name">String</span> s3 <span class="token operator">=</span> <span class="token string">&quot;ab&quot;</span><span class="token punctuation">;</span>
<span class="token class-name">String</span> s4 <span class="token operator">=</span> s1 <span class="token operator">+</span> s2<span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br></div></div></li> <li><p>进行反编译(只展示部分),根据如下执行的指令,可以发现</p> <ul><li><code>String s4 = s1 + s2;</code> 是通过 <code>new StringBuilder().append(&quot;a&quot;).append(&quot;b&quot;).toString()</code> 来进行组成的字符串</li> <li>StringBuilder 中的 toString 方法就是通过 <code>new String(&quot;ab&quot;)</code> 在堆空间中创建一个对象</li> <li><strong>以上即可得出 <code>s3 == s4</code> 不成立s3 变量指向串池s4 变量指向堆中的对象</strong></li></ul> <div class="language-tex line-numbers-mode"><pre class="language-tex"><code>Constant pool:
#2 = String #30 // a
#3 = String #31 // b
#4 = String #32 // ab
#5 = Class #33 // java/lang/StringBuilder
#6 = Methodref #5.#29 // java/lang/StringBuilder.&quot;&lt;init&gt;&quot;:()V
#7 = Methodref #5.#34 // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
#8 = Methodref #5.#35 // java/lang/StringBuilder.toString:()Ljava/lang/String;
public static void main(java.lang.String<span class="token punctuation">[</span><span class="token punctuation">]</span>);
0: ldc #2 // String a
2: astore_1
3: ldc #3 // String b
5: astore_2
6: ldc #4 // String ab
8: astore_3
9: new #5 // class java/lang/StringBuilder
12: dup
13: invokespecial #6 // Method java/lang/StringBuilder.&quot;&lt;init&gt;&quot;:()V
16: aload_1
17: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
20: aload_2
21: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
27: astore 4
29: return
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br></div></div></li></ul> <h5 id="_5-5-3-常量拼接"><a href="#_5-5-3-常量拼接" class="header-anchor">#</a> 5.5.3 常量拼接</h5> <ul><li><p>现对源代码进行改变如下,需要了解字符串常量拼接的原理,以及 <code>s3 == s5</code> 是否成立</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Demo1_9</span> <span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token class-name">String</span> s1 <span class="token operator">=</span> <span class="token string">&quot;a&quot;</span><span class="token punctuation">;</span>
<span class="token class-name">String</span> s2 <span class="token operator">=</span> <span class="token string">&quot;b&quot;</span><span class="token punctuation">;</span>
<span class="token class-name">String</span> s3 <span class="token operator">=</span> <span class="token string">&quot;ab&quot;</span><span class="token punctuation">;</span>
<span class="token class-name">String</span> s4 <span class="token operator">=</span> s1 <span class="token operator">+</span> s2<span class="token punctuation">;</span>
<span class="token class-name">String</span> s5 <span class="token operator">=</span> <span class="token string">&quot;a&quot;</span> <span class="token operator">+</span> <span class="token string">&quot;b&quot;</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br></div></div></li> <li><p>进行反编译(只展示部分),根据如下执行的指令,可以发现</p> <ul><li><code>String s5 = &quot;a&quot; + &quot;b&quot;;</code> 直接往 StringTable 串池中查找 &quot;ab&quot;</li> <li>发生以上的原因javac 在编译期间的优化,结果已经在编译器确定为 &quot;ab&quot;</li> <li><strong>即得出 <code>s3 == s5</code> 成立s3 和 s5 都是来自串池中的 &quot;ab&quot;,属于同一个对象</strong></li></ul> <div class="language-tex line-numbers-mode"><pre class="language-tex"><code>public static void main(java.lang.String<span class="token punctuation">[</span><span class="token punctuation">]</span>);
0: ldc #2 // String a
2: astore_1
3: ldc #3 // String b
5: astore_2
6: ldc #4 // String ab
8: astore_3
9: new #5 // class java/lang/StringBuilder
12: dup
13: invokespecial #6 // Method java/lang/StringBuilder.&quot;&lt;init&gt;&quot;:()V
16: aload_1
17: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
20: aload_2
21: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
27: astore 4
29: ldc #4 // String ab
31: astore 5
33: return
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br></div></div></li></ul> <h5 id="_5-5-4-intern-jdk1-8"><a href="#_5-5-4-intern-jdk1-8" class="header-anchor">#</a> 5.5.4 intern (JDK1.8)</h5> <ul><li><p>现对源代码进行改变如下,需要了解 String.intern() 方法的原理,以及 <code>s6 == s7</code> 是否成立</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Demo1_9</span> <span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token class-name">String</span> s6 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">(</span><span class="token string">&quot;c&quot;</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">(</span><span class="token string">&quot;d&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">String</span> s7 <span class="token operator">=</span> <span class="token string">&quot;cd&quot;</span><span class="token punctuation">;</span>
<span class="token class-name">String</span> intern <span class="token operator">=</span> s6<span class="token punctuation">.</span><span class="token function">intern</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div></li> <li><p>进行反编译(只展示部分),根据如下执行的指令,可以发现</p> <ul><li>通过 <code>new String(&quot;c&quot;)</code> 方法,会在堆内存和常量池中各创建一份数据,然后通过 变量拼接 的方式在堆内存中生成一份 <code>cd</code>,但此数据没有入串池中</li> <li><code>String s7 = &quot;cd&quot;;</code> 此时串池中没有存在该数据,所以入串池操作</li> <li><code>s6.intern();</code> 将该字符串对象尝试放入串池,如果有则不会放入,如果没有则放入串池,会把串池中的对象返回</li> <li><strong>程序执行完后,会发现 s6 属于堆内存中的对象,而 s7 属于串池中的对象,所以 <code>s6 == s7</code> 不成立</strong></li></ul> <div class="language-tex line-numbers-mode"><pre class="language-tex"><code>public static void main(java.lang.String<span class="token punctuation">[</span><span class="token punctuation">]</span>);
0: new #2 // class java/lang/StringBuilder
3: dup
4: invokespecial #3 // Method java/lang/StringBuilder.&quot;&lt;init&gt;&quot;:()V
7: new #4 // class java/lang/String
10: dup
11: ldc #5 // String c
13: invokespecial #6 // Method java/lang/String.&quot;&lt;init&gt;&quot;:(Ljava/lang/String;)V
16: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: new #4 // class java/lang/String
22: dup
23: ldc #8 // String d
25: invokespecial #6 // Method java/lang/String.&quot;&lt;init&gt;&quot;:(Ljava/lang/String;)V
28: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
31: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
34: astore_1
35: ldc #10 // String cd
37: astore_2
38: aload_1
39: invokevirtual #11 // Method java/lang/String.intern:()Ljava/lang/String;
42: astore_3
43: return
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br></div></div></li></ul> <blockquote><p>思考:</p> <p> 将以上代码修改为以下代码,<code>s6 == s7</code> 又会是怎样的结果?</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Demo1_9</span> <span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token class-name">String</span> s6 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">(</span><span class="token string">&quot;c&quot;</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">(</span><span class="token string">&quot;d&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">String</span> intern <span class="token operator">=</span> s6<span class="token punctuation">.</span><span class="token function">intern</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">String</span> s7 <span class="token operator">=</span> <span class="token string">&quot;cd&quot;</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><p>分析:</p> <ol><li>执行完 s6 变量赋值过后,常量池和堆的数据如下
<ul><li>StringTable [&quot;c&quot;, &quot;d&quot;]</li> <li>Heap ['new String(&quot;c&quot;)', 'new String(&quot;d&quot;)', 'new String(&quot;cd&quot;)']</li></ul></li> <li>执行完 s6.intern 后,会判断常量池中是否存在 &quot;cd&quot;
<ul><li>StringTable [&quot;c&quot;, &quot;d&quot;, &quot;cd&quot;] <em>实际上这里的 &quot;cd&quot; 是堆中 'new String(&quot;cd&quot;)' 的一个引用,是用一个对象</em></li> <li>Heap ['new String(&quot;c&quot;)', 'new String(&quot;d&quot;)', 'new String(&quot;cd&quot;)']</li></ul></li> <li>执行到 s7 时,发现常量池中存在了 &quot;cd&quot;,所以直接引用</li></ol> <p><strong>综上所述,当前情况下的 <code>s6 == s7</code> 是成立的</strong></p></blockquote> <p></p> <h5 id="_5-5-5-intern-jdk1-6"><a href="#_5-5-5-intern-jdk1-6" class="header-anchor">#</a> 5.5.5 intern (JDK1.6)</h5> <p>在 JDK1.6 中,以下执行的结果与 JDK1.8 的相同: <code>s6 == s7</code> 不成立</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Demo1_9</span> <span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token class-name">String</span> s6 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">(</span><span class="token string">&quot;c&quot;</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">(</span><span class="token string">&quot;d&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">String</span> s7 <span class="token operator">=</span> <span class="token string">&quot;cd&quot;</span><span class="token punctuation">;</span>
<span class="token class-name">String</span> intern <span class="token operator">=</span> s6<span class="token punctuation">.</span><span class="token function">intern</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><p>但是当改为以下代码时,结果却不尽相同</p> <ol><li>执行完 s6 变量赋值过后,常量池和堆的数据如下
<ul><li>StringTable [&quot;c&quot;, &quot;d&quot;]</li> <li>Heap ['new String(&quot;c&quot;)', 'new String(&quot;d&quot;)', 'new String(&quot;cd&quot;)']</li></ul></li> <li>执行完 s6.intern 后,会判断常量池中是否存在 &quot;cd&quot;,如果不存在,那么将 <strong>s6 拷贝一份放入常量池中,并返回</strong> <ul><li>StringTable [&quot;c&quot;, &quot;d&quot;, &quot;cd&quot;] <em>在此时,&quot;cd&quot; 和 'new String(&quot;cd&quot;)' 不是同一个对象,因为是通过拷贝的方式</em></li> <li>Heap ['new String(&quot;c&quot;)', 'new String(&quot;d&quot;)', 'new String(&quot;cd&quot;)']</li></ul></li> <li>执行到 s7 时,发现常量池中存在了 &quot;cd&quot;,所以直接引用</li></ol> <p><strong>综上所述,当前情况下的 <code>s6 == s7</code> 是不成立的</strong></p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Demo1_9</span> <span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token class-name">String</span> s6 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">(</span><span class="token string">&quot;c&quot;</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">(</span><span class="token string">&quot;d&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">String</span> intern <span class="token operator">=</span> s6<span class="token punctuation">.</span><span class="token function">intern</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">String</span> s7 <span class="token operator">=</span> <span class="token string">&quot;cd&quot;</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br></div></div><h5 id="_5-5-6-特性总结"><a href="#_5-5-6-特性总结" class="header-anchor">#</a> 5.5.6 特性总结</h5> <ul><li><p>常量池中的字符串仅是符号,第一次用到时才变为对象</p></li> <li><p>利用串池的机制,来避免重复创建字符串对象</p></li> <li><p>字符串变量拼接的原理是 StringBuilder1.8</p></li> <li><p>字符串常量拼接的原理是编译期优化</p></li> <li><p>可以使用 intern 方法,主动将串池中还没有的字符串对象放入串池</p> <ul><li>1.8 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串池中的对象返回</li> <li>1.6 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有会把此对象<strong>复制</strong>一份,放入串池,会把串池中的对象返回</li></ul></li></ul> <h4 id="_5-6-stringtable-面试题"><a href="#_5-6-stringtable-面试题" class="header-anchor">#</a> 5.6 StringTable 面试题</h4> <p>学习完 StringTable 字符串常量池后,能正确的理解回答下面的题目,那么恭喜你掌握了一个新的知识!</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token class-name">String</span> s1 <span class="token operator">=</span> <span class="token string">&quot;a&quot;</span><span class="token punctuation">;</span>
<span class="token class-name">String</span> s2 <span class="token operator">=</span> <span class="token string">&quot;b&quot;</span><span class="token punctuation">;</span>
<span class="token class-name">String</span> s3 <span class="token operator">=</span> <span class="token string">&quot;a&quot;</span> <span class="token operator">+</span> <span class="token string">&quot;b&quot;</span><span class="token punctuation">;</span>
<span class="token class-name">String</span> s4 <span class="token operator">=</span> s1 <span class="token operator">+</span> s2<span class="token punctuation">;</span>
<span class="token class-name">String</span> s5 <span class="token operator">=</span> <span class="token string">&quot;ab&quot;</span><span class="token punctuation">;</span>
<span class="token class-name">String</span> s6 <span class="token operator">=</span> s4<span class="token punctuation">.</span><span class="token function">intern</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 问</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>s3 <span class="token operator">==</span> s4<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>s3 <span class="token operator">==</span> s5<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>s3 <span class="token operator">==</span> s6<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">String</span> x2 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">(</span><span class="token string">&quot;c&quot;</span><span class="token punctuation">)</span> <span class="token operator">+</span> <span class="token keyword">new</span> <span class="token class-name">String</span><span class="token punctuation">(</span><span class="token string">&quot;d&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">String</span> x1 <span class="token operator">=</span> <span class="token string">&quot;cd&quot;</span><span class="token punctuation">;</span>
x2<span class="token punctuation">.</span><span class="token function">intern</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 问如果调换了【最后两行代码】的位置呢如果是jdk1.6呢?</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>x1 <span class="token operator">==</span> x2<span class="token punctuation">)</span><span class="token punctuation">;</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br></div></div><p>答案:</p> <div class="language-tex line-numbers-mode"><pre class="language-tex"><code>false true true
jdk1.8: false true
jdk1.6: false false
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br></div></div><h4 id="_5-7-stringtable-位置"><a href="#_5-7-stringtable-位置" class="header-anchor">#</a> 5.7 StringTable 位置</h4> <p>在 JDK1.6 时StringTable 是常量池的一部分,常量池存储在永久代中(在永久代中需要进行 Full GC 才会进行垃圾回收,回收效率低)</p> <p>在 JDK1.8 时StringTable 是堆中的一部分,垃圾回收跟随堆进行,回收效率高</p> <p>怎么证明以上两个版本存储的位置不相同?</p> <ul><li>在 JDK1.6 中,如果常量池中发生了内存溢出,那么就会报错 <code>java.lang.OutOfMemoryError: PermGen space</code>,永久代内存溢出</li> <li>在 JDK1.8 中,如果常量池中发生了内存溢出,那么就会报错 <code>java.lang.OutOfMemoryError: Java heap space</code>,堆内存溢出</li></ul> <p>通过以上思路,来证明 JDK1.6 和 JDK1.8 StringTable 常量池的位置不同</p> <p>案例代码</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Demo1_10</span> <span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">String</span><span class="token punctuation">&gt;</span></span> list <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
<span class="token keyword">try</span> <span class="token punctuation">{</span>
<span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> j <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> j <span class="token operator">&lt;</span> <span class="token number">260000</span><span class="token punctuation">;</span> j<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
list<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">.</span><span class="token function">valueOf</span><span class="token punctuation">(</span>j<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">intern</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
i<span class="token operator">++</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Throwable</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br></div></div><p><strong>JDK1.8 验证</strong>:需要设置堆内存的大小 <code>-Xmx10m</code>,会发现有以下结果,报错信息和预想的结果不一样?</p> <ul><li>原来,在 JVM规范中 当GC垃圾回收花了 98% 的时间只释放了 2% 的堆空间时JVM 就会认为该程序没救了,就直接抛出该异常</li> <li>那么要跳过这个限制,需要再多配置一个参数 <code>-XX:-UseGCOverheadLimit</code></li></ul> <div class="language-tex line-numbers-mode"><pre class="language-tex"><code>java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.lang.Integer.toString(Integer.java:403)
at java.lang.String.valueOf(String.java:3099)
at com.zhuhjay.Demo1_10.main(Demo1_10.java:16)
145367
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br></div></div><p>最终结果就是预想的结果了!</p> <div class="language-tex line-numbers-mode"><pre class="language-tex"><code>*** java.lang.instrument ASSERTION FAILED ***: &quot;!errorOutstanding&quot; with message can't create byte arrau at JPLISAgent.c line: 813
java.lang.OutOfMemoryError: Java heap space
at java.lang.Integer.toString(Integer.java:401)
at java.lang.String.valueOf(String.java:3099)
at com.zhuhjay.Demo1_10.main(Demo1_10.java:16)
146837
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><p><strong>JDK1.6 验证</strong>:需要设置永久代内存的大小 <code>-XX:MaxPermSize=10m</code></p> <h4 id="_5-8-stringtable-垃圾回收"><a href="#_5-8-stringtable-垃圾回收" class="header-anchor">#</a> 5.8 StringTable 垃圾回收</h4> <p>查看字符串常量池 StringTable 的垃圾回收是否会在空间不足时进行字符串常量池的垃圾回收</p> <p>示例代码,为了更进一步查看堆内存和字符串常量池的空间,需要添加以下参数</p> <ul><li><code>-Xmx10m</code>设置堆的大小为了方便测试GC垃圾回收</li> <li><code>-XX:+PrintStringTableStatistics</code>:打印字符串表的信息</li> <li><code>-XX:+PrintGCDetails -verbose:gc</code>:统计垃圾回收的信息</li></ul> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Demo1_11</span> <span class="token punctuation">{</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
<span class="token keyword">try</span> <span class="token punctuation">{</span>
<span class="token comment">// TODO 待插入代码</span>
<span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Throwable</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br></div></div><p>查看输出结果,认识一下下面所代表的含义</p> <div class="language-tex line-numbers-mode"><pre class="language-tex"><code>Heap # 堆内存的占用情况
PSYoungGen total 2560K, used 1676K <span class="token punctuation">[</span>0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)
eden space 2048K, 81<span class="token comment">% used [0x00000000ffd00000,0x00000000ffea3198,0x00000000fff00000)</span>
from space 512K, 0<span class="token comment">% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)</span>
to space 512K, 0<span class="token comment">% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)</span>
ParOldGen total 7168K, used 0K <span class="token punctuation">[</span>0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)
object space 7168K, 0<span class="token comment">% used [0x00000000ff600000,0x00000000ff600000,0x00000000ffd00000)</span>
Metaspace used 3167K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 345K, capacity 388K, committed 512K, reserved 1048576K
SymbolTable statistics: # 符号表的详细信息(也是常量池的一部分,但是并不关心这部分内容)
Number of buckets : 20011 = 160088 bytes, avg 8.000
Number of entries : 13260 = 318240 bytes, avg 24.000
Number of literals : 13260 = 568232 bytes, avg 42.853
Total footprint : = 1046560 bytes
Average bucket size : 0.663
Variance of bucket size : 0.666
Std. dev. of bucket size: 0.816
Maximum bucket size : 6
StringTable statistics: # StringTable 统计信息,使用 hashtable 结构
Number of buckets : 60013 = 480104 bytes, avg 8.000 # 桶个数 = 字节数
Number of entries : 1739 = 41736 bytes, avg 24.000 # 键值对个数 = 字节数
Number of literals : 1739 = 156592 bytes, avg 90.047 # 字符串常量个数 = 字节数
Total footprint : = 678432 bytes # 总的空间
Average bucket size : 0.029
Variance of bucket size : 0.029
Std. dev. of bucket size: 0.171
Maximum bucket size : 3
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br></div></div><p>现在在以上代码中插入以下代码将10000个字符串添加到字符串常量池中此时字符串常量池中理应会在原来的 1739 基础上多 10000</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">for</span> <span class="token punctuation">(</span><span class="token keyword">int</span> j <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> j <span class="token operator">&lt;</span> <span class="token number">10000</span><span class="token punctuation">;</span> j<span class="token operator">++</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token class-name">String</span><span class="token punctuation">.</span><span class="token function">valueOf</span><span class="token punctuation">(</span>j<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">intern</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
i<span class="token operator">++</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br></div></div><p>可以看到此时运行就有了 GC垃圾回收 的信息,此时字符串常量池没有达到预期的效果,说明当常量池中有不被使用的数据会被垃圾回收</p> <div class="language-tex line-numbers-mode"><pre class="language-tex"><code><span class="token punctuation">[</span>GC (Allocation Failure) <span class="token punctuation">[</span>PSYoungGen: 2048K-&gt;488K(2560K)<span class="token punctuation">]</span> 2048K-&gt;704K(9728K), 0.0031150 secs<span class="token punctuation">]</span> <span class="token punctuation">[</span>Times: user=0.00 sys=0.00, real=0.00 secs<span class="token punctuation">]</span>
10000
Heap
...省略展示
SymbolTable statistics:
...省略展示
StringTable statistics:
Number of buckets : 60013 = 480104 bytes, avg 8.000
Number of entries : 4026 = 96624 bytes, avg 24.000
Number of literals : 4026 = 266888 bytes, avg 66.291
Total footprint : = 843616 bytes
Average bucket size : 0.067
Variance of bucket size : 0.065
Std. dev. of bucket size: 0.256
Maximum bucket size : 3
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br></div></div><h4 id="_5-9-stringtable-性能调优"><a href="#_5-9-stringtable-性能调优" class="header-anchor">#</a> 5.9 StringTable 性能调优</h4> <ul><li><p>字符串常量池的性能随着分配桶的个数越多,效率越高,参数配置 <code>-XX:StringTableSize=1009</code>,配置最小值为 1009</p></li> <li><p>String.intern 调优了一大部分性能,防止滋生很多的重复数据</p></li></ul> <h2 id="_6-直接内存"><a href="#_6-直接内存" class="header-anchor">#</a> 6 直接内存</h2> <p>直接内存是系统内存</p> <h3 id="_6-1-定义"><a href="#_6-1-定义" class="header-anchor">#</a> 6.1 定义</h3> <p>Direct Memory</p> <ul><li>常见于 NIO 操作时,用于数据缓冲区</li> <li>分配回收成本较高,但读写性能高</li> <li>不受 JVM 内存回收管理</li></ul> <h3 id="_6-2-使用直接内存"><a href="#_6-2-使用直接内存" class="header-anchor">#</a> 6.2 使用直接内存</h3> <h4 id="_6-2-1-测试io与direct-memory读写效率"><a href="#_6-2-1-测试io与direct-memory读写效率" class="header-anchor">#</a> 6.2.1 测试IO与Direct Memory读写效率</h4> <p>使用差不多 1G 的文件来进行读写对比</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Demo1_12</span> <span class="token punctuation">{</span>
<span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token class-name">String</span> <span class="token constant">FROM</span> <span class="token operator">=</span> <span class="token string">&quot;G:\\maven\\maven_repository_0.zip&quot;</span><span class="token punctuation">;</span>
<span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token class-name">String</span> <span class="token constant">TO</span> <span class="token operator">=</span> <span class="token string">&quot;G:\\IDM_DownLoad\\Video\\Temp\\maven.zip&quot;</span><span class="token punctuation">;</span>
<span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">int</span> _1MB <span class="token operator">=</span> <span class="token number">1024</span> <span class="token operator">*</span> <span class="token number">1024</span><span class="token punctuation">;</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token function">io</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token function">directBuffer</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">directBuffer</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">long</span> start <span class="token operator">=</span> <span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">nanoTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">try</span> <span class="token punctuation">(</span><span class="token class-name">FileChannel</span> from <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FileInputStream</span><span class="token punctuation">(</span><span class="token constant">FROM</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getChannel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">FileChannel</span> <span class="token keyword">to</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FileOutputStream</span><span class="token punctuation">(</span><span class="token constant">TO</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">getChannel</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token class-name">ByteBuffer</span> bb <span class="token operator">=</span> <span class="token class-name">ByteBuffer</span><span class="token punctuation">.</span><span class="token function">allocateDirect</span><span class="token punctuation">(</span>_1MB<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">int</span> length <span class="token operator">=</span> from<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span>bb<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>length <span class="token operator">==</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">break</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
bb<span class="token punctuation">.</span><span class="token function">flip</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">to</span><span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span>bb<span class="token punctuation">)</span><span class="token punctuation">;</span>
bb<span class="token punctuation">.</span><span class="token function">clear</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">long</span> end <span class="token operator">=</span> <span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">nanoTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">&quot;directBuffer 用时:&quot;</span> <span class="token operator">+</span> <span class="token punctuation">(</span>end <span class="token operator">-</span> start<span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token number">1000_000.0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">io</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">long</span> start <span class="token operator">=</span> <span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">nanoTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">try</span> <span class="token punctuation">(</span><span class="token class-name">FileInputStream</span> from <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FileInputStream</span><span class="token punctuation">(</span><span class="token constant">FROM</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">FileOutputStream</span> <span class="token keyword">to</span> <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">FileOutputStream</span><span class="token punctuation">(</span><span class="token constant">TO</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">byte</span><span class="token punctuation">[</span><span class="token punctuation">]</span> buf <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token keyword">byte</span><span class="token punctuation">[</span>_1MB<span class="token punctuation">]</span><span class="token punctuation">;</span>
<span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">int</span> length <span class="token operator">=</span> from<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span>buf<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">if</span> <span class="token punctuation">(</span>length <span class="token operator">==</span> <span class="token operator">-</span><span class="token number">1</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token keyword">break</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">to</span><span class="token punctuation">.</span><span class="token function">write</span><span class="token punctuation">(</span>buf<span class="token punctuation">,</span> <span class="token number">0</span><span class="token punctuation">,</span> length<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span> <span class="token keyword">catch</span> <span class="token punctuation">(</span><span class="token class-name">Exception</span> e<span class="token punctuation">)</span> <span class="token punctuation">{</span>
e<span class="token punctuation">.</span><span class="token function">printStackTrace</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">long</span> end <span class="token operator">=</span> <span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">nanoTime</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">&quot;io 用时:&quot;</span> <span class="token operator">+</span> <span class="token punctuation">(</span>end <span class="token operator">-</span> start<span class="token punctuation">)</span> <span class="token operator">/</span> <span class="token number">1000_000.0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br><span class="line-number">21</span><br><span class="line-number">22</span><br><span class="line-number">23</span><br><span class="line-number">24</span><br><span class="line-number">25</span><br><span class="line-number">26</span><br><span class="line-number">27</span><br><span class="line-number">28</span><br><span class="line-number">29</span><br><span class="line-number">30</span><br><span class="line-number">31</span><br><span class="line-number">32</span><br><span class="line-number">33</span><br><span class="line-number">34</span><br><span class="line-number">35</span><br><span class="line-number">36</span><br><span class="line-number">37</span><br><span class="line-number">38</span><br><span class="line-number">39</span><br><span class="line-number">40</span><br><span class="line-number">41</span><br><span class="line-number">42</span><br><span class="line-number">43</span><br><span class="line-number">44</span><br><span class="line-number">45</span><br><span class="line-number">46</span><br><span class="line-number">47</span><br><span class="line-number">48</span><br><span class="line-number">49</span><br><span class="line-number">50</span><br><span class="line-number">51</span><br><span class="line-number">52</span><br></div></div><p>对比结果如下单位ms可以发现使用直接内存读写效率更高</p> <div class="language-tex line-numbers-mode"><pre class="language-tex"><code>io 用时:19611.177
directBuffer 用时:5490.16
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br></div></div><p><img src="/jvm/1669128719697.png" alt="1669128719697"></p> <ul><li>Java的 io 操作涉及到 CPU 的用户态和内核态
<ul><li>io 操作需要操作系统从磁盘中进行磁盘文件的读取,将磁盘文件读取到系统内存中的系统缓存区</li> <li>此时 Java 不能直接操作该缓存区,需要在 Java 堆内存中开辟一块 java缓冲区byte[]</li> <li>然后将 系统缓冲区 的数据写到 Java缓冲区 中才能进一步操作</li></ul></li></ul> <p><img src="/jvm/1669128690245.png" alt="1669128690245"></p> <ul><li>调用 <code>ByteBuffer bb = ByteBuffer.allocateDirect(_1MB);</code> 时,会在 系统内存 和 Java堆内存 中开辟一块 <code>direct Memory</code> 内存来进行使用,这就是直接内存,使得 Java 可以直接操作</li></ul> <h4 id="_6-2-2-直接内存溢出"><a href="#_6-2-2-直接内存溢出" class="header-anchor">#</a> 6.2.2 直接内存溢出</h4> <p>每次分配 100M 内存,直到 Direct buffer memory 溢出</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Demo1_13</span> <span class="token punctuation">{</span>
<span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">int</span> _100MB <span class="token operator">=</span> <span class="token number">1024</span> <span class="token operator">*</span> <span class="token number">1024</span> <span class="token operator">*</span> <span class="token number">100</span><span class="token punctuation">;</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token class-name">List</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token class-name">ByteBuffer</span><span class="token punctuation">&gt;</span></span> list <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ArrayList</span><span class="token generics"><span class="token punctuation">&lt;</span><span class="token punctuation">&gt;</span></span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">int</span> i <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span>
<span class="token keyword">try</span> <span class="token punctuation">{</span>
<span class="token keyword">while</span> <span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span> <span class="token punctuation">{</span>
<span class="token class-name">ByteBuffer</span> byteBuffer <span class="token operator">=</span> <span class="token class-name">ByteBuffer</span><span class="token punctuation">.</span><span class="token function">allocateDirect</span><span class="token punctuation">(</span>_100MB<span class="token punctuation">)</span><span class="token punctuation">;</span>
list<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span>byteBuffer<span class="token punctuation">)</span><span class="token punctuation">;</span>
i<span class="token operator">++</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>i<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br></div></div><p>可以看到直接内存分配到大约 1.7G 时就溢出了</p> <div class="language-tex line-numbers-mode"><pre class="language-tex"><code>17
Exception in thread &quot;main&quot; java.lang.OutOfMemoryError: Direct buffer memory
at java.nio.Bits.reserveMemory(Bits.java:695)
at java.nio.DirectByteBuffer.&lt;init&gt;(DirectByteBuffer.java:123)
at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)
at com.zhuhjay.Demo1_13.main(Demo1_13.java:19)
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br></div></div><h4 id="_6-2-3-直接内存释放"><a href="#_6-2-3-直接内存释放" class="header-anchor">#</a> 6.2.3 直接内存释放</h4> <p>查看以下案例,分配 1GB 的内存,然后使用 GC 进行回收</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Demo1_14</span> <span class="token punctuation">{</span>
<span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">int</span> _1GB <span class="token operator">=</span> <span class="token number">1024</span> <span class="token operator">*</span> <span class="token number">1024</span> <span class="token operator">*</span> <span class="token number">1024</span><span class="token punctuation">;</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">IOException</span> <span class="token punctuation">{</span>
<span class="token class-name">ByteBuffer</span> byteBuffer <span class="token operator">=</span> <span class="token class-name">ByteBuffer</span><span class="token punctuation">.</span><span class="token function">allocateDirect</span><span class="token punctuation">(</span>_1GB<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">&quot;分配完毕...&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>in<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">&quot;开始释放...&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
byteBuffer <span class="token operator">=</span> <span class="token keyword">null</span><span class="token punctuation">;</span>
<span class="token class-name">System</span><span class="token punctuation">.</span><span class="token function">gc</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>in<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br></div></div><p>结果如图所示,这样一来感觉好像可以被 GC垃圾回收 进行 直接内存 的回收,但其实并不然。</p> <p><img src="/jvm/1669130149269.png" alt="1669130149269"></p> <p>接下来 Unsafe 脱离 JVM 的内存管理来进行 直接内存 分配</p> <div class="language-java line-numbers-mode"><pre class="language-java"><code><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Demo1_15</span> <span class="token punctuation">{</span>
<span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token keyword">final</span> <span class="token keyword">int</span> _1GB <span class="token operator">=</span> <span class="token number">1024</span> <span class="token operator">*</span> <span class="token number">1024</span> <span class="token operator">*</span> <span class="token number">1024</span><span class="token punctuation">;</span>
<span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Exception</span> <span class="token punctuation">{</span>
<span class="token class-name">Unsafe</span> unsafe <span class="token operator">=</span> <span class="token function">getUnsafe</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 分配内存,并返回分配到的内存首地址</span>
<span class="token keyword">long</span> base <span class="token operator">=</span> unsafe<span class="token punctuation">.</span><span class="token function">allocateMemory</span><span class="token punctuation">(</span>_1GB<span class="token punctuation">)</span><span class="token punctuation">;</span>
unsafe<span class="token punctuation">.</span><span class="token function">setMemory</span><span class="token punctuation">(</span>base<span class="token punctuation">,</span> _1GB<span class="token punctuation">,</span> <span class="token punctuation">(</span><span class="token keyword">byte</span><span class="token punctuation">)</span> <span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>in<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token comment">// 手动释放内存</span>
unsafe<span class="token punctuation">.</span><span class="token function">freeMemory</span><span class="token punctuation">(</span>base<span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token class-name">System</span><span class="token punctuation">.</span>in<span class="token punctuation">.</span><span class="token function">read</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token class-name">Unsafe</span> <span class="token function">getUnsafe</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">Exception</span> <span class="token punctuation">{</span>
<span class="token class-name">Field</span> field <span class="token operator">=</span> <span class="token class-name">Unsafe</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">.</span><span class="token function">getDeclaredField</span><span class="token punctuation">(</span><span class="token string">&quot;theUnsafe&quot;</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
field<span class="token punctuation">.</span><span class="token function">setAccessible</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token class-name">Unsafe</span><span class="token punctuation">)</span> field<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span>
<span class="token punctuation">}</span>
</code></pre> <div class="line-numbers-wrapper"><span class="line-number">1</span><br><span class="line-number">2</span><br><span class="line-number">3</span><br><span class="line-number">4</span><br><span class="line-number">5</span><br><span class="line-number">6</span><br><span class="line-number">7</span><br><span class="line-number">8</span><br><span class="line-number">9</span><br><span class="line-number">10</span><br><span class="line-number">11</span><br><span class="line-number">12</span><br><span class="line-number">13</span><br><span class="line-number">14</span><br><span class="line-number">15</span><br><span class="line-number">16</span><br><span class="line-number">17</span><br><span class="line-number">18</span><br><span class="line-number">19</span><br><span class="line-number">20</span><br></div></div><p>可以发现使用 直接内存 是通过 Unsafe 进行管理的GC垃圾回收 只能对 JVM内存有效</p> <p><img src="/jvm/1669130788224.png" alt="1669130788224"></p> <p>查看 <code>ByteBuffer.allocateDirect()</code> 方法的源码</p> <ul><li>使用了 Unsafe 进行 直接内存的分配</li> <li>使用了 <code>Cleaner</code> 特殊对象,虚引用 <code>PhantomReference&lt;Object&gt;</code> 对象</li> <li>虚引用对象 <code>Cleaner</code> 所关联的实际对象 <code>ByteBuffer</code> 被 GC垃圾回收之后就会主动调用回调对象 <code>Deallocator</code> 主动的释放 直接内存</li></ul> <p><img src="/jvm/1669131731576.png" alt="1669131731576"></p> <h3 id="_6-3-分配和回收原理"><a href="#_6-3-分配和回收原理" class="header-anchor">#</a> 6.3 分配和回收原理</h3> <ul><li>使用了 Unsafe 对象完成直接内存的分配回收,并且回收需要主动调用 freeMemory 方法</li> <li>ByteBuffer 的实现类内部,使用了 Cleaner虚引用来监测 ByteBuffer 对象,一旦 ByteBuffer 对象被垃圾回收,那么就会由 ReferenceHandler 线程通过 Cleaner 的 clean 方法调 用 freeMemory 来释放直接内存</li></ul> <p>禁用显示回收对直接内存的影响(就是禁用代码层面的 <code>System.gc()</code>,使其无效)</p> <ul><li><code>System.gc()</code> 触发的是 <code>Full GC</code>,比较影响性能的,不光回收新生代,还会回收老年代,会使得程序暂停时间较长</li> <li>使用虚拟机参数 <code>-XX:+DisableExplicitGC</code></li> <li>代码层面的 GC 失效后,<code>ByteBuffer</code> 得不到内存的释放,使得 直接内存 也无法被释放,需要等到下一次 GC 时才会触发</li> <li><strong>问题解决</strong>:手动调用 Unsafe 释放直接内存</li></ul> <p><img src="/jvm/1669132766803.png" alt="1669132766803"></p></div></section> <footer class="page-edit"><!----> <!----></footer> <div class="page-nav"><p class="inner"><!----> <span class="next"><a href="/blogs/java/jvm/JVM-垃圾回收.html">
JVM之垃圾回收
</a></span></p></div> <div class="comments-wrapper"><!----></div></main></div> <!----></div> <ul class="sub-sidebar sub-sidebar-wrapper" style="width:12rem;" data-v-b57cc07c data-v-7dd95ae2><li class="level-2" data-v-b57cc07c><a href="/blogs/java/jvm/JVM-%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.html#_1-程序计数器" class="sidebar-link reco-side-_1-程序计数器" data-v-b57cc07c>1 程序计数器</a></li><li class="level-2" data-v-b57cc07c><a href="/blogs/java/jvm/JVM-%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.html#_2-虚拟机栈" class="sidebar-link reco-side-_2-虚拟机栈" data-v-b57cc07c>2 虚拟机栈</a></li><li class="level-3" data-v-b57cc07c><a href="/blogs/java/jvm/JVM-%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.html#_2-1-定义" class="sidebar-link reco-side-_2-1-定义" data-v-b57cc07c>2.1 定义</a></li><li class="level-3" data-v-b57cc07c><a href="/blogs/java/jvm/JVM-%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.html#_2-2-栈内存溢出" class="sidebar-link reco-side-_2-2-栈内存溢出" data-v-b57cc07c>2.2 栈内存溢出</a></li><li class="level-3" data-v-b57cc07c><a href="/blogs/java/jvm/JVM-%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.html#_2-3-线程运行诊断" class="sidebar-link reco-side-_2-3-线程运行诊断" data-v-b57cc07c>2.3 线程运行诊断</a></li><li class="level-2" data-v-b57cc07c><a href="/blogs/java/jvm/JVM-%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.html#_3-本地方法栈" class="sidebar-link reco-side-_3-本地方法栈" data-v-b57cc07c>3 本地方法栈</a></li><li class="level-2" data-v-b57cc07c><a href="/blogs/java/jvm/JVM-%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.html#_4-堆" class="sidebar-link reco-side-_4-堆" data-v-b57cc07c>4 堆</a></li><li class="level-3" data-v-b57cc07c><a href="/blogs/java/jvm/JVM-%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.html#_4-1-定义" class="sidebar-link reco-side-_4-1-定义" data-v-b57cc07c>4.1 定义</a></li><li class="level-3" data-v-b57cc07c><a href="/blogs/java/jvm/JVM-%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.html#_4-2-堆内存溢出" class="sidebar-link reco-side-_4-2-堆内存溢出" data-v-b57cc07c>4.2 堆内存溢出</a></li><li class="level-3" data-v-b57cc07c><a href="/blogs/java/jvm/JVM-%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.html#_4-3-堆内存诊断" class="sidebar-link reco-side-_4-3-堆内存诊断" data-v-b57cc07c>4.3 堆内存诊断</a></li><li class="level-2" data-v-b57cc07c><a href="/blogs/java/jvm/JVM-%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.html#_5-方法区" class="sidebar-link reco-side-_5-方法区" data-v-b57cc07c>5 方法区</a></li><li class="level-3" data-v-b57cc07c><a href="/blogs/java/jvm/JVM-%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.html#_5-1-定义" class="sidebar-link reco-side-_5-1-定义" data-v-b57cc07c>5.1 定义</a></li><li class="level-3" data-v-b57cc07c><a href="/blogs/java/jvm/JVM-%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.html#_5-2-组成" class="sidebar-link reco-side-_5-2-组成" data-v-b57cc07c>5.2 组成</a></li><li class="level-3" data-v-b57cc07c><a href="/blogs/java/jvm/JVM-%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.html#_5-3-方法区内存溢出" class="sidebar-link reco-side-_5-3-方法区内存溢出" data-v-b57cc07c>5.3 方法区内存溢出</a></li><li class="level-3" data-v-b57cc07c><a href="/blogs/java/jvm/JVM-%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.html#_5-4-运行时常量池" class="sidebar-link reco-side-_5-4-运行时常量池" data-v-b57cc07c>5.4 运行时常量池</a></li><li class="level-3" data-v-b57cc07c><a href="/blogs/java/jvm/JVM-%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.html#_5-5-stringtable" class="sidebar-link reco-side-_5-5-stringtable" data-v-b57cc07c>5.5 StringTable</a></li><li class="level-2" data-v-b57cc07c><a href="/blogs/java/jvm/JVM-%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.html#_6-直接内存" class="sidebar-link reco-side-_6-直接内存" data-v-b57cc07c>6 直接内存</a></li><li class="level-3" data-v-b57cc07c><a href="/blogs/java/jvm/JVM-%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.html#_6-1-定义" class="sidebar-link reco-side-_6-1-定义" data-v-b57cc07c>6.1 定义</a></li><li class="level-3" data-v-b57cc07c><a href="/blogs/java/jvm/JVM-%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.html#_6-2-使用直接内存" class="sidebar-link reco-side-_6-2-使用直接内存" data-v-b57cc07c>6.2 使用直接内存</a></li><li class="level-3" data-v-b57cc07c><a href="/blogs/java/jvm/JVM-%E5%86%85%E5%AD%98%E7%BB%93%E6%9E%84.html#_6-3-分配和回收原理" class="sidebar-link reco-side-_6-3-分配和回收原理" data-v-b57cc07c>6.3 分配和回收原理</a></li></ul></div></div></div><div class="global-ui"><div class="back-to-ceiling" style="right:1rem;bottom:6rem;width:2.5rem;height:2.5rem;border-radius:.25rem;line-height:2.5rem;display:none;" data-v-c6073ba8 data-v-c6073ba8><svg t="1574745035067" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5404" class="icon" data-v-c6073ba8><path d="M526.60727968 10.90185116a27.675 27.675 0 0 0-29.21455937 0c-131.36607665 82.28402758-218.69155461 228.01873535-218.69155402 394.07834331a462.20625001 462.20625001 0 0 0 5.36959153 69.94390903c1.00431239 6.55289093-0.34802892 13.13561351-3.76865779 18.80351572-32.63518765 54.11355614-51.75690182 118.55860487-51.7569018 187.94566865a371.06718723 371.06718723 0 0 0 11.50484808 91.98906777c6.53300375 25.50556257 41.68394495 28.14064038 52.69160883 4.22606766 17.37162448-37.73630017 42.14135425-72.50938081 72.80769204-103.21549295 2.18761121 3.04276886 4.15646224 6.24463696 6.40373557 9.22774369a1871.4375 1871.4375 0 0 0 140.04691725 5.34970492 1866.36093723 1866.36093723 0 0 0 140.04691723-5.34970492c2.24727335-2.98310674 4.21612437-6.18497483 6.3937923-9.2178004 30.66633723 30.70611158 55.4360664 65.4791928 72.80769147 103.21549355 11.00766384 23.91457269 46.15860503 21.27949489 52.69160879-4.22606768a371.15156223 371.15156223 0 0 0 11.514792-91.99901164c0-69.36717486-19.13165746-133.82216804-51.75690182-187.92578088-3.42062944-5.66790279-4.76302748-12.26056868-3.76865837-18.80351632a462.20625001 462.20625001 0 0 0 5.36959269-69.943909c-0.00994388-166.08943902-87.32547796-311.81420293-218.6915546-394.09823051zM605.93803103 357.87693858a93.93749974 93.93749974 0 1 1-187.89594924 6.1e-7 93.93749974 93.93749974 0 0 1 187.89594924-6.1e-7z" p-id="5405" data-v-c6073ba8></path><path d="M429.50777625 765.63860547C429.50777625 803.39355007 466.44236686 1000.39046097 512.00932183 1000.39046097c45.56695499 0 82.4922232-197.00623328 82.5015456-234.7518555 0-37.75494459-36.9345906-68.35043303-82.4922232-68.34111062-45.57627738-0.00932239-82.52019037 30.59548842-82.51086798 68.34111062z" p-id="5406" data-v-c6073ba8></path></svg></div></div></div>
<script src="/assets/js/app.049e1b5b.js" defer></script><script src="/assets/js/3.ff0e945d.js" defer></script><script src="/assets/js/1.1ced4111.js" defer></script><script src="/assets/js/18.a55369bc.js" defer></script>
</body>
</html>