- 新增 .drone.yml 文件用于定义 CI/CD 流程 - 配置了基于 Docker 的部署步骤 - 设置了工作区和卷映射以支持持久化数据 - 添加了构建准备阶段和 Docker 部署阶段 - 定义了环境变量和代理设置 - 配置了 artifacts 目录的处理逻辑 - 添加了 timezone 映射以确保时间同步 - 设置了 docker.sock 映射以支持 Docker in Docker
600 lines
141 KiB
HTML
600 lines
141 KiB
HTML
<!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:对象 -> 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>eg1:CPU占用过多</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">"1.."</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">"2.."</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">"3.."</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">></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"><</span><span class="token class-name">Student</span><span class="token punctuation">></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"><</span><span class="token punctuation">></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"><</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"><</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">"Class"</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">"java/lang/Object"</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">"Class"</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 "main" 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">"hello world"</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 "Demo1_8.java"
|
||
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."<init>":()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 <init>
|
||
#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 // "<init>":()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."<init>":()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: "Demo1_8.java"
|
||
</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("hello world")</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("a")</code> 会被放入字符串常量池和堆中,返回值是堆中的引用</li> <li><code>"a"</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">"a"</span><span class="token punctuation">;</span>
|
||
<span class="token class-name">String</span> s2 <span class="token operator">=</span> <span class="token string">"b"</span><span class="token punctuation">;</span>
|
||
<span class="token class-name">String</span> s3 <span class="token operator">=</span> <span class="token string">"ab"</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 符号变为 "a" 字符串对象,然后到 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">"a"</span><span class="token punctuation">;</span>
|
||
<span class="token class-name">String</span> s2 <span class="token operator">=</span> <span class="token string">"b"</span><span class="token punctuation">;</span>
|
||
<span class="token class-name">String</span> s3 <span class="token operator">=</span> <span class="token string">"ab"</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("a").append("b").toString()</code> 来进行组成的字符串</li> <li>StringBuilder 中的 toString 方法就是通过 <code>new String("ab")</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."<init>":()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."<init>":()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">"a"</span><span class="token punctuation">;</span>
|
||
<span class="token class-name">String</span> s2 <span class="token operator">=</span> <span class="token string">"b"</span><span class="token punctuation">;</span>
|
||
<span class="token class-name">String</span> s3 <span class="token operator">=</span> <span class="token string">"ab"</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">"a"</span> <span class="token operator">+</span> <span class="token string">"b"</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 = "a" + "b";</code> 直接往 StringTable 串池中查找 "ab"</li> <li>发生以上的原因:javac 在编译期间的优化,结果已经在编译器确定为 "ab"</li> <li><strong>即得出 <code>s3 == s5</code> 成立,s3 和 s5 都是来自串池中的 "ab",属于同一个对象</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."<init>":()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">"c"</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">"d"</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">"cd"</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("c")</code> 方法,会在堆内存和常量池中各创建一份数据,然后通过 变量拼接 的方式在堆内存中生成一份 <code>cd</code>,但此数据没有入串池中</li> <li><code>String s7 = "cd";</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."<init>":()V
|
||
7: new #4 // class java/lang/String
|
||
10: dup
|
||
11: ldc #5 // String c
|
||
13: invokespecial #6 // Method java/lang/String."<init>":(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."<init>":(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">"c"</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">"d"</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">"cd"</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 ["c", "d"]</li> <li>Heap ['new String("c")', 'new String("d")', 'new String("cd")']</li></ul></li> <li>执行完 s6.intern 后,会判断常量池中是否存在 "cd"
|
||
<ul><li>StringTable ["c", "d", "cd"] (<em>实际上这里的 "cd" 是堆中 'new String("cd")' 的一个引用,是用一个对象</em>)</li> <li>Heap ['new String("c")', 'new String("d")', 'new String("cd")']</li></ul></li> <li>执行到 s7 时,发现常量池中存在了 "cd",所以直接引用</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">"c"</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">"d"</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">"cd"</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 ["c", "d"]</li> <li>Heap ['new String("c")', 'new String("d")', 'new String("cd")']</li></ul></li> <li>执行完 s6.intern 后,会判断常量池中是否存在 "cd",如果不存在,那么将 <strong>s6 拷贝一份放入常量池中,并返回</strong> <ul><li>StringTable ["c", "d", "cd"] (<em>在此时,"cd" 和 'new String("cd")' 不是同一个对象,因为是通过拷贝的方式</em>)</li> <li>Heap ['new String("c")', 'new String("d")', 'new String("cd")']</li></ul></li> <li>执行到 s7 时,发现常量池中存在了 "cd",所以直接引用</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">"c"</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">"d"</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">"cd"</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>字符串变量拼接的原理是 StringBuilder(1.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">"a"</span><span class="token punctuation">;</span>
|
||
<span class="token class-name">String</span> s2 <span class="token operator">=</span> <span class="token string">"b"</span><span class="token punctuation">;</span>
|
||
<span class="token class-name">String</span> s3 <span class="token operator">=</span> <span class="token string">"a"</span> <span class="token operator">+</span> <span class="token string">"b"</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">"ab"</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">"c"</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">"d"</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">"cd"</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"><</span><span class="token class-name">String</span><span class="token punctuation">></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"><</span><span class="token punctuation">></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"><</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 ***: "!errorOutstanding" 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"><</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->488K(2560K)<span class="token punctuation">]</span> 2048K->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">"G:\\maven\\maven_repository_0.zip"</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">"G:\\IDM_DownLoad\\Video\\Temp\\maven.zip"</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">"directBuffer 用时:"</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">"io 用时:"</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"><</span><span class="token class-name">ByteBuffer</span><span class="token punctuation">></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"><</span><span class="token punctuation">></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 "main" java.lang.OutOfMemoryError: Direct buffer memory
|
||
at java.nio.Bits.reserveMemory(Bits.java:695)
|
||
at java.nio.DirectByteBuffer.<init>(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">"分配完毕..."</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">"开始释放..."</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">"theUnsafe"</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<Object></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>
|