-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy path6ead94ff.html
More file actions
429 lines (322 loc) · 46.6 KB
/
6ead94ff.html
File metadata and controls
429 lines (322 loc) · 46.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222"><meta name="generator" content="Hexo 6.3.0">
<link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
<link rel="icon" type="image/png" sizes="32x32" href="/images/favicon.png">
<link rel="icon" type="image/png" sizes="16x16" href="/images/favicon.png">
<link rel="mask-icon" href="/images/logo.svg" color="#222">
<link rel="stylesheet" href="/css/main.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" integrity="sha256-HtsXJanqjKTc8vVQjO4YMhiqFoXkfBsjBWcX91T1jr8=" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">
<script class="next-config" data-name="main" type="application/json">{"hostname":"tallate.github.io","root":"/","images":"/images","scheme":"Gemini","darkmode":false,"version":"8.18.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":{"enable":false,"style":null},"fold":{"enable":false,"height":500},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":false,"transition":{"menu_item":"fadeInDown","post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果:${query}","hits_time":"找到 ${hits} 个搜索结果(用时 ${time} 毫秒)","hits":"找到 ${hits} 个搜索结果"}}</script><script src="/js/config.js"></script>
<meta name="description" content="栈帧结构栈帧主要包括了局部变量表、操作数栈、动态连接、方法返回地址等信息,在内存结构章节中我们已经探讨过栈结构,但是还未从实现层面来讨论过。 局部变量表用于存放方法参数和方法内部的局部变量。局部变量表的大小在方法的 Code 属性中就已经定义好了,为max_locals的值,局部变量表的单位为slot,32位以内的类型只占用一个slot(包括 returnAddress 类型),64 位">
<meta property="og:type" content="article">
<meta property="og:title" content="JVM 与执行引擎">
<meta property="og:url" content="https://tallate.github.io/6ead94ff.html">
<meta property="og:site_name" content="Tallate">
<meta property="og:description" content="栈帧结构栈帧主要包括了局部变量表、操作数栈、动态连接、方法返回地址等信息,在内存结构章节中我们已经探讨过栈结构,但是还未从实现层面来讨论过。 局部变量表用于存放方法参数和方法内部的局部变量。局部变量表的大小在方法的 Code 属性中就已经定义好了,为max_locals的值,局部变量表的单位为slot,32位以内的类型只占用一个slot(包括 returnAddress 类型),64 位">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://tallate.github.io/imgs/JVM/%E5%8A%A8%E6%80%81%E5%88%86%E6%B4%BE%E7%9A%84%E5%AE%9E%E7%8E%B0.png">
<meta property="article:published_time" content="2019-09-26T07:26:49.000Z">
<meta property="article:modified_time" content="2025-07-06T17:56:20.864Z">
<meta property="article:author" content="tallate">
<meta property="article:tag" content="JVM">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="https://tallate.github.io/imgs/JVM/%E5%8A%A8%E6%80%81%E5%88%86%E6%B4%BE%E7%9A%84%E5%AE%9E%E7%8E%B0.png">
<link rel="canonical" href="https://tallate.github.io/6ead94ff.html">
<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":false,"isPost":true,"lang":"zh-CN","comments":true,"permalink":"https://tallate.github.io/6ead94ff.html","path":"/6ead94ff.html","title":"JVM 与执行引擎"}</script>
<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>JVM 与执行引擎 | Tallate</title>
<noscript>
<link rel="stylesheet" href="/css/noscript.css">
</noscript>
</head>
<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
<div class="headband"></div>
<main class="main">
<div class="column">
<header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="site-brand-container">
<div class="site-nav-toggle">
<div class="toggle" aria-label="切换导航栏" role="button">
<span class="toggle-line"></span>
<span class="toggle-line"></span>
<span class="toggle-line"></span>
</div>
</div>
<div class="site-meta">
<a href="/" class="brand" rel="start">
<i class="logo-line"></i>
<p class="site-title">Tallate</p>
<i class="logo-line"></i>
</a>
<p class="site-subtitle" itemprop="description">该吃吃该喝喝 啥事别往心里搁</p>
</div>
<div class="site-nav-right">
<div class="toggle popup-trigger" aria-label="搜索" role="button">
<i class="fa fa-search fa-fw fa-lg"></i>
</div>
</div>
</div>
<nav class="site-nav">
<ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="home fa-fw"></i>首页</a></li><li class="menu-item menu-item-about"><a href="/about/" rel="section"><i class="user fa-fw"></i>关于</a></li><li class="menu-item menu-item-tags"><a href="/tags/" rel="section"><i class="tags fa-fw"></i>标签<span class="badge">84</span></a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="th fa-fw"></i>分类<span class="badge">25</span></a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="archive fa-fw"></i>归档<span class="badge">192</span></a></li>
<li class="menu-item menu-item-search">
<a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
</a>
</li>
</ul>
</nav>
<div class="search-pop-overlay">
<div class="popup search-popup"><div class="search-header">
<span class="search-icon">
<i class="fa fa-search"></i>
</span>
<div class="search-input-container">
<input autocomplete="off" autocapitalize="off" maxlength="80"
placeholder="搜索..." spellcheck="false"
type="search" class="search-input">
</div>
<span class="popup-btn-close" role="button">
<i class="fa fa-times-circle"></i>
</span>
</div>
<div class="search-result-container no-result">
<div class="search-result-icon">
<i class="fa fa-spinner fa-pulse fa-5x"></i>
</div>
</div>
</div>
</div>
</header>
<aside class="sidebar">
<div class="sidebar-inner sidebar-nav-active sidebar-toc-active">
<ul class="sidebar-nav">
<li class="sidebar-nav-toc">
文章目录
</li>
<li class="sidebar-nav-overview">
站点概览
</li>
</ul>
<div class="sidebar-panel-container">
<!--noindex-->
<div class="post-toc-wrap sidebar-panel">
<div class="post-toc animated"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%A0%88%E5%B8%A7%E7%BB%93%E6%9E%84"><span class="nav-number">1.</span> <span class="nav-text">栈帧结构</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#%E6%96%B9%E6%B3%95%E7%9A%84%E8%B0%83%E7%94%A8%E5%92%8C%E6%89%A7%E8%A1%8C"><span class="nav-number">2.</span> <span class="nav-text">方法的调用和执行</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#%E8%A7%A3%E6%9E%90"><span class="nav-number">2.1.</span> <span class="nav-text">解析</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%88%86%E6%B4%BE"><span class="nav-number">2.2.</span> <span class="nav-text">分派</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#%E9%9D%99%E6%80%81%E5%88%86%E6%B4%BE"><span class="nav-number">2.2.1.</span> <span class="nav-text">静态分派</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%8A%A8%E6%80%81%E5%88%86%E6%B4%BE"><span class="nav-number">2.2.2.</span> <span class="nav-text">动态分派</span></a></li><li class="nav-item nav-level-4"><a class="nav-link" href="#%E5%8D%95%E5%88%86%E6%B4%BE%E4%B8%8E%E5%A4%9A%E5%88%86%E6%B4%BE"><span class="nav-number">2.2.3.</span> <span class="nav-text">单分派与多分派</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E6%96%B9%E6%B3%95%E9%80%80%E5%87%BA"><span class="nav-number">2.3.</span> <span class="nav-text">方法退出</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#%E5%8A%A8%E6%80%81%E7%B1%BB%E5%9E%8B%E8%AF%AD%E8%A8%80%E6%94%AF%E6%8C%81"><span class="nav-number">2.4.</span> <span class="nav-text">动态类型语言支持</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#QA"><span class="nav-number">3.</span> <span class="nav-text">QA</span></a></li></ol></div>
</div>
<!--/noindex-->
<div class="site-overview-wrap sidebar-panel">
<div class="site-author animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
<p class="site-author-name" itemprop="name">tallate</p>
<div class="site-description" itemprop="description"></div>
</div>
<div class="site-state-wrap animated">
<nav class="site-state">
<div class="site-state-item site-state-posts">
<a href="/archives/">
<span class="site-state-item-count">192</span>
<span class="site-state-item-name">日志</span>
</a>
</div>
<div class="site-state-item site-state-categories">
<a href="/categories/">
<span class="site-state-item-count">25</span>
<span class="site-state-item-name">分类</span></a>
</div>
<div class="site-state-item site-state-tags">
<a href="/tags/">
<span class="site-state-item-count">84</span>
<span class="site-state-item-name">标签</span></a>
</div>
</nav>
</div>
</div>
</div>
</div>
</aside>
</div>
<div class="main-inner post posts-expand">
<div class="post-block">
<article itemscope itemtype="http://schema.org/Article" class="post-content" lang="zh-CN">
<link itemprop="mainEntityOfPage" href="https://tallate.github.io/6ead94ff.html">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="image" content="/images/avatar.gif">
<meta itemprop="name" content="tallate">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="Tallate">
<meta itemprop="description" content="">
</span>
<span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
<meta itemprop="name" content="JVM 与执行引擎 | Tallate">
<meta itemprop="description" content="">
</span>
<header class="post-header">
<h1 class="post-title" itemprop="name headline">
JVM 与执行引擎
</h1>
<div class="post-meta-container">
<div class="post-meta">
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="创建时间:2019-09-26 15:26:49" itemprop="dateCreated datePublished" datetime="2019-09-26T15:26:49+08:00">2019-09-26</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-calendar-check"></i>
</span>
<span class="post-meta-item-text">更新于</span>
<time title="修改时间:2025-07-07 01:56:20" itemprop="dateModified" datetime="2025-07-07T01:56:20+08:00">2025-07-07</time>
</span>
<span class="post-meta-item">
<span class="post-meta-item-icon">
<i class="far fa-folder"></i>
</span>
<span class="post-meta-item-text">分类于</span>
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/Java/" itemprop="url" rel="index"><span itemprop="name">Java</span></a>
</span>
</span>
</div>
</div>
</header>
<div class="post-body" itemprop="articleBody"><span id="more"></span>
<h2 id="栈帧结构"><a href="#栈帧结构" class="headerlink" title="栈帧结构"></a>栈帧结构</h2><p>栈帧主要包括了局部变量表、操作数栈、动态连接、方法返回地址等信息,在内存结构章节中我们已经探讨过栈结构,但是还未从实现层面来讨论过。</p>
<ol>
<li>局部变量表<br>用于存放方法参数和方法内部的局部变量。局部变量表的大小在方法的 Code 属性中就已经定义好了,为<strong>max_locals</strong>的值,局部变量表的单位为<strong>slot</strong>,32位以内的类型只占用一个<strong>slot</strong>(包括 returnAddress 类型),64 位的类型占用两个 slot。<ul>
<li>对于实例方法而言,索引为 0 的 slot 存放的是 this 引用,之后再依次存放方法参数、局部变量;</li>
<li>slot 可以被重用,当局部变量已经超出了作用域时,在作用域外再定义局部变量时,可以重用之前的 slot 空间。</li>
<li>同时,局部变量没有赋值是不能够使用的——会产生编译错误,这和类变量和实例变量是有不同的,如下面代码: <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">public void test() {</span><br><span class="line"> int i;</span><br><span class="line"> System.out.println(i);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li>
</ul>
</li>
<li>操作数栈<br>执行方法时,存放操作数的栈,栈的深度在方法的 Code 属性中已经定义好了,为<strong>max_stack</strong>的值,32 位以内的类型占用一个栈单位,64 位的类型占用两个栈单位。操作数栈可以与其他栈的局部变量表共享区域,这样可以共用一部分数据。</li>
<li>动态连接<br>动态连接是为了支持在运行期间将符号引用转化为直接引用的操作。我们知道,每一个方法对应一个栈帧,而每一个栈帧,都包含指向对应方法的引用,这个引用就是为了支持动态连接,如 invokedynamic 指令。动态连接与静态解析对应,静态解析是在类加载(解析阶段)或者第一次使用时将符号引用转化为直接引用,动态连接则是每一次运行的时候都需要进行转化(invokedynamic 指令)。</li>
<li>方法返回地址<br>正常方法返回,返回地址为到调用该方法的指令的下一条指令的地址;异常返回,返回地址由异常表确定。方法返回时,需要恢复上层方法的局部变量表、操作数栈、将返回值压入调用者栈帧的操作数栈、设置 PC 值。</li>
</ol>
<h2 id="方法的调用和执行"><a href="#方法的调用和执行" class="headerlink" title="方法的调用和执行"></a>方法的调用和执行</h2><p>方法<strong>调用</strong>决定了调用哪个方法,并创建对应的栈帧,接下来会开始方法的<strong>执行</strong>。</p>
<h3 id="解析"><a href="#解析" class="headerlink" title="解析"></a>解析</h3><p>在程序执行前就已经确定了方法调用的<strong>版本</strong>,即编译期就确定了调用方法版本,这个版本在运行时是不可变的。</p>
<ul>
<li><strong>静态方法</strong>、<strong>私有方法</strong>、<strong>final方法</strong>在编译时就可以确定具体的调用版本,静态方法直接与类型相关、私有方法在外部不可访问、final 不可被继承,也可唯一确定,这些方法称为<strong>非虚方法</strong>,翻译成字节码是 invokestatic(调用静态方法)、invokespecial(调用实例构造器<init>方法、私有方法、父类方法),在类加载的解析阶段就可以进行解析,如下方法调用在编译期就可以确定方法调用的版本。 <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">class Father {</span><br><span class="line"> public static void print(String str) {</span><br><span class="line"> System.out.println("father " + str);</span><br><span class="line"> }</span><br><span class="line"> private void show(String str) {</span><br><span class="line"> System.out.println("father " + str);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">class Son extends Father {</span><br><span class="line">}</span><br><span class="line">public class Test {</span><br><span class="line"> public static void main(String[] args) {</span><br><span class="line"> Son.print("coder"); // 调用的是Father的print()方法</span><br><span class="line"> //Father fa = new Father();</span><br><span class="line"> //fa.show("cooooder"); // 私有方法无法调用</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li>
<li>其他方法称为<strong>虚方法</strong>。</li>
</ul>
<h3 id="分派"><a href="#分派" class="headerlink" title="分派"></a>分派</h3><p>分派调用与多态密切相关,分为<strong>静态分派</strong>、<strong>动态分派</strong>、<strong>单分派</strong>、<strong>多分派</strong>。</p>
<h4 id="静态分派"><a href="#静态分派" class="headerlink" title="静态分派"></a>静态分派</h4><p>与静态分派相关的就是方法的<strong>重载</strong>,重载时根据参数的静态类型引用类型而非实际类型决定调用哪个版本。<br>选取的过程共分为三个阶段:</p>
<ol>
<li>在不考虑对基本类型自动装拆箱(auto-boxing,auto-unboxing),以及可变长参数的情况下选取重载方法;</li>
<li>如果在第 1 个阶段中没有找到适配的方法,那么在允许自动装拆箱,但不允许可变长参数的情况下选取重载方法;</li>
<li>如果在第 2 个阶段中没有找到适配的方法,那么在允许自动装拆箱以及可变长参数的情况下选取重载方法。</li>
</ol>
<p>如果 Java 编译器在同一个阶段中找到了多个适配的方法,那么它会在其中选择一个最为贴切的,而决定贴切程度的一个关键就是形式参数类型的继承关系。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">/**</span><br><span class="line"> * 重载方法在编译器就可以进行确定,不需要等到运行期间</span><br><span class="line"> */</span><br><span class="line">public class StaticDispatch {</span><br><span class="line"> static class Human {</span><br><span class="line"> }</span><br><span class="line"> static class Women extends Human {</span><br><span class="line"> }</span><br><span class="line"> static class Men extends Human {</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public void sayHello(Human human) {</span><br><span class="line"> System.out.println("say human");</span><br><span class="line"> }</span><br><span class="line"> public void sayHello(Women women) {</span><br><span class="line"> System.out.println("say women");</span><br><span class="line"> }</span><br><span class="line"> public void sayHello(Men men) {</span><br><span class="line"> System.out.println("say men");</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public static void main(String[] args) {</span><br><span class="line"> StaticDispatch ds = new StaticDispatch();</span><br><span class="line"> Human women = new Women();</span><br><span class="line"> Human men = new Men();</span><br><span class="line"> // 编译时确定方法的调用版本是以Human作为参数的方法</span><br><span class="line"> ds.sayHello(women);</span><br><span class="line"> ds.sayHello(men);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h4 id="动态分派"><a href="#动态分派" class="headerlink" title="动态分派"></a>动态分派</h4><p>与动态分派相关的就是方法的<strong>重写</strong>,在子类中我们会重写父类的方法,而在调用的时候根据实际类型来选择适合的调用版本。<br> <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line">public class DynamicDispatch {</span><br><span class="line"> abstract static class Human {</span><br><span class="line"> abstract public void sayHello();</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> static class Women extends Human {</span><br><span class="line"> @Override</span><br><span class="line"> public void sayHello() {</span><br><span class="line"> System.out.println("say women");</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> static class Men extends Human {</span><br><span class="line"> @Override</span><br><span class="line"> public void sayHello() {</span><br><span class="line"> System.out.println("say men");</span><br><span class="line"> }</span><br><span class="line"> } </span><br><span class="line"></span><br><span class="line"> public static void main(String[] args) {</span><br><span class="line"> Human women = new Women();</span><br><span class="line"> Human men = new Men();</span><br><span class="line"> women.sayHello(); // 实际类型是Women</span><br><span class="line"> men.sayHello(); // 实际类型是Men</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<h4 id="单分派与多分派"><a href="#单分派与多分派" class="headerlink" title="单分派与多分派"></a>单分派与多分派</h4><p>方法的接收者(方法的所有者)与方法的参数统称为方法的<strong>宗量</strong>,根据分派基于多少种宗量,可以将分派划分为<strong>单分派</strong>和<strong>多分派</strong>。<br>单分派根据一个宗量确定调用方法的版本;多分派根据多个宗量确定调用方法的版本。<br> <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line">public class Dispatch {</span><br><span class="line"> static class QQ {};</span><br><span class="line"> static class _360{};</span><br><span class="line"></span><br><span class="line"> public static class Father {</span><br><span class="line"> public void hardChoice(QQ arg) {</span><br><span class="line"> System.out.println("father choose qq");</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public void hardChoice(_360 arg) {</span><br><span class="line"> System.out.println("father choose 360");</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public static class Son extends Father {</span><br><span class="line"> public void hardChoice(QQ arg) {</span><br><span class="line"> System.out.println("son choose qq");</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public void hardChoice(_360 arg) {</span><br><span class="line"> System.out.println("son choose 360");</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public static void main(String[] args) {</span><br><span class="line"> Father father = new Father();</span><br><span class="line"> Father son = new Son();</span><br><span class="line"> father.hardChoice(new _360());</span><br><span class="line"> son.hardChoice(new QQ());</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>静态分派过程如下,在编译期阶段,会根据静态类型与参数类型确定调用版本,产生两条分别指向 Father.hardChoice(QQ)和 Father.hardChoice(_360)的指令,可以知道,在编译期,是由多个宗量确定调用版本,是静态多分派。<br>动态分派过程如下,在运行期,在执行 hardChoice(QQ)或者 hardChoice(_360)时,已经确定了参数必须为 QQ、_360,方法签名确定,静态类型和实际类型此时都不会对方法本身产生任何影响,而虚拟机会根据实际类型来确定调用版本,只根据一个宗量进行确定,因此,在运行时,是动态单分派。<br>在面向对象编程中我们会很频繁地使用到动态分配,虚拟机采用在类的方法区建立一个<strong>虚方法表(非虚方法不会出现在表中)</strong>来实现。<br><img src="/imgs/JVM/%E5%8A%A8%E6%80%81%E5%88%86%E6%B4%BE%E7%9A%84%E5%AE%9E%E7%8E%B0.png" alt="动态分派的实现" title="动态分派的实现"></p>
<ul>
<li>只有<strong>虚方法</strong>才会出现在虚方法表中,也就是说<strong>静态方法</strong>、<strong>私有方法</strong>、<strong>final 方法</strong>是不会出现在这张表中的。</li>
<li>从 Object 类继承的方法都会指向 Object 类型数据中各方法的实际入口地址。</li>
<li>类自身的方法会指向类的数据类型中方法的实际入口地址。</li>
<li>父类的没有被重写的方法在虚方法表中的索引与子类方法表中的索引相同,这样,当类型变化时,只需要改变方法表就行,索引还是相同。</li>
<li>方法表一般在类加载的连接阶段进行初始化,准备了类变量的初始值后,方法表也初始化完毕。</li>
</ul>
<h3 id="方法退出"><a href="#方法退出" class="headerlink" title="方法退出"></a>方法退出</h3><p>当一个方法开始执行后,只有两种方式可以退出:</p>
<ol>
<li>第一种方式是执行引擎遇到任意一个方法返回的字节码指令,这种方式称为<strong>正常完成出口</strong>;</li>
<li>另外一种退出方式是,在方法执行过程中遇到异常,且该异常没有被被捕获,称为<strong>异常完成出口</strong>。</li>
</ol>
<p>无论是哪种退出方式,在方法退出后,都需要返回到该方法被调用的位置(地址),让程序继续执行。一般来说,方法执行前,会保存调用者当前的 PC 计数器中的值,当方法正常退出时,将该 PC 计数器的值会作为返回地址,返回给调用者。在方法异常退出时,返回地址是通过<strong>异常处理器表</strong>来确定的。</p>
<p>方法退出的过程实际上就等于把当前栈帧出栈,一般过程为:</p>
<ol>
<li>恢复上层方法的局部变量表和操作数栈</li>
<li>把返回值压入调用者栈帧的操作数栈中</li>
<li>调整 PC 计数器的值,以指向方法调用指令后面的一条指令</li>
</ol>
<h3 id="动态类型语言支持"><a href="#动态类型语言支持" class="headerlink" title="动态类型语言支持"></a>动态类型语言支持</h3><p>Java 是一种静态类型语言,它与 Python、JavaScript 等动态类型语言的主要区别是:</p>
<ul>
<li>静态类型语言的类型检查主要过程是在编译期进行而不是运行期。</li>
</ul>
<p>静态类型语言与动态类型语言的比较如下:</p>
<ul>
<li>静态类型语言在编译期确定类型,最显著的好处是编译器可以提供严谨的类型检查,这样与类型相关的问题能在编码的时候就及时发现,利于稳定性及代码达到更大规模。</li>
<li>动态类型语言在运行期确定类型,这可以为开发人员提供更大的灵活性,某些在静态类型语言中需用大量“臃肿”代码来实现的功能,由动态类型语言来实现可能会更加清晰和简洁,从而提升开发效率。</li>
</ul>
<p>在 JDK1.7 以前的字节码指令集中,4 条方法调用指令(invokevirtual、invokespecial、invokestatic、invokeinterface)的第一个参数都是被调用的方法的符号引用(CONSTANT_Methodref_info 或者 CONSTANT_InterfaceMethodref_info 常量),前面已经提到过,方法的符号引用在编译时产生,而动态类型语言只有在运行期才能确定接收者类型。<br>Java 不像 C/C++那样有 Function Pointer 或者 C#里的 Delegate。在 Java 中要实现类似的功能可以有以下两种方式:</p>
<ol>
<li>实现一个函数接口,比如 Comparator</li>
<li>MethodHandle,它的实现原理是第 5 条方法调用的字节码指令 invokedynamic,与其他 invoke*指令的最大差别是它的分派逻辑不是由虚拟机决定的,而是由程序员决定的。</li>
</ol>
<p>MethodHandle 的例子:</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line">import java.lang.invoke.MethodHandle;</span><br><span class="line">import java.lang.invoke.MethodType;</span><br><span class="line"></span><br><span class="line">import static java.lang.invoke.MethodHandles.lookup;</span><br><span class="line"></span><br><span class="line">/**</span><br><span class="line"> * @author hgc</span><br><span class="line"> * @date 2/16/20</span><br><span class="line"> */</span><br><span class="line">public class MethodHandleTest {</span><br><span class="line"></span><br><span class="line"> static class ClassA {</span><br><span class="line"> public void println(String s) {</span><br><span class="line"> System.out.println(s);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> private static MethodHandle getPrintlnMH(Object reveiver) throws NoSuchMethodException, IllegalAccessException {</span><br><span class="line"> // MethodType代表方法类型,包含了方法的返回值(methodType()的第一个参数)和具体参数(methodType()第二个及之后的参数)</span><br><span class="line"> MethodType mt = MethodType.methodType(void.class, String.class);</span><br><span class="line"> // lookup()方法来自于MethodHandles.lookup,这句的作用是在指定类中查找符合给定方法名称、方法类型,并且符合调用权限的方法句柄</span><br><span class="line"> // 因为这里调用的是一个虚方法,按照Java语言的规则,方法第一个参数是隐式的,代表该方法的接收者,也即是this指向的对象,这个参数以前是放在参数列表中进行传递的,而现在提供了bindTo()方法来完成这件事</span><br><span class="line"> return lookup().findVirtual(reveiver.getClass(), "println", mt).bindTo(reveiver);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public static void main(String[] args) throws Throwable {</span><br><span class="line"> Object obj = System.currentTimeMillis() % 2 == 0 ? System.out : new ClassA();</span><br><span class="line"> getPrintlnMH(obj).invokeExact("icyfenix");</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>MethodHandle 与反射(Reflection)的区别是:</p>
<ul>
<li>Reflection API 的设计初衷是只为 Java 服务,而 MethodHandle 则设计为可服务于所有 Java 虚拟机之上的语言,当然也包括 Java 语言。</li>
<li>MethodHandle 与 Reflection 机制都是在模拟方法调用,但 Reflection 是在模拟代码层次的方法调用,而 MethodHandle 则是在模拟字节码层次的方法调用。<br>MethodHandles.lookup 中的 3 个方法——findStatic()、findVirtual()、findSpecial()对应了 invokestatic、invokevirtual + invokeinterface 和 invokespecial 这几条字节码指令的执行权限校验行为,而这些底层细节在使用 Reflection API 时无需考虑。</li>
<li>Reflection 中的 Method 比 MethodHandle 对象包含的信息多,Reflection 是重量级的,MethodHandle 是轻量级的。</li>
<li>MethodHandle 模拟了字节码的方法指令调用,所以理论上虚拟机在这方面做的各种优化(如方法内联)在 MethodHandle 上也可以采用类似思路来支持,而通过反射去调用方法则不行。</li>
</ul>
<p>至于 MethodHandle 是如何实现的,可以参考《深入理解 Java 虚拟机》,大致上就是运行期去常量表里根据用户指定的参数找方法。</p>
<p>###基于栈的字节码解释执行引擎<br>JVM 的指令都是基于栈的,比如<code>iadd</code>表示弹出栈顶的两个元素,然后求出二者的和后重新压入栈中。<br>基于栈的指令集与基于寄存器的指令集各有优势:</p>
<ul>
<li>基于栈的指令集的主要优点就是可移植。<br>寄存器是由硬件直接提供的,基于寄存器的指令集由于直接依赖这些硬件寄存器则不可避免地要受到硬件的约束。<br>如果使用栈架构的指令集,用户程序不会直接使用这些寄存器,就可以由虚拟机实现来自行决定把一些访问最频繁的数据(程序计数器、栈顶缓存等)放到寄存器中以获取尽量好的性能,这样实现起来也更加简单一些。</li>
<li>栈架构的指令集还有一些其他优点,比如代码相对来说更加紧凑(字节码中每个字节就对应一条指令,而多地址指令集中还需要存放参数)、编译器实现更加简单(不需要考虑空间分配的问题,所需空间都在栈上操作)等。</li>
<li>栈架构指令集的主要缺点是执行速度相对来说会稍慢一些。所有主流物理机的指令集都是寄存器架构也从侧面印证了这一点。<br>一方面,虽然栈架构指令集的代码非常紧凑,但是完成相同功能所需的指令数量一般会比寄存器架构多。比如出栈入栈操作本身就产生了相当多的指令数量。<br>另一方面,栈实现在内存之中,频繁的栈访问也就意味着频繁的内存访问,相对于处理器来说,内存始终是执行速度的瓶颈。尽管虚拟机可以采取栈顶缓存的手段,把最常用的操作映射到寄存器中避免直接内存访问,但这也只能是优化措施而不是解决本质问题的方法。</li>
</ul>
<h2 id="QA"><a href="#QA" class="headerlink" title="QA"></a>QA</h2><ol>
<li>哪个方法会被调用?<br>重载会触发静态分派,会根据传参的静态类型来决定调用哪个方法,因此会调用 print(Father),但输出时调用了 Child 类的 toString 方法,因为方法被重写了,会触发方法的动态分派,根据传参的实际类型来决定调用哪个方法。 <figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line">public class DynamicDispatchTest {</span><br><span class="line"></span><br><span class="line"> public void print(Father father) {</span><br><span class="line"> System.out.println(father);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public void print(Child child) {</span><br><span class="line"> System.out.println(child);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public static class Father {</span><br><span class="line"></span><br><span class="line"> @Override</span><br><span class="line"> public String toString() {</span><br><span class="line"> return "Father";</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public static class Child extends Father {</span><br><span class="line"></span><br><span class="line"> @Override</span><br><span class="line"> public String toString() {</span><br><span class="line"> return "Child";</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> public static void main(String[] args) {</span><br><span class="line"> Father father = new Child();</span><br><span class="line"> DynamicDispatchTest dynamicDispatchTest = new DynamicDispatchTest();</span><br><span class="line"> dynamicDispatchTest.print(father);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li>
</ol>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/kity.min.js"></script><script type="text/javascript" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/kityminder.core.min.js"></script><script defer="true" type="text/javascript" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/mindmap.min.js"></script><link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/mindmap.min.css">
</div>
<footer class="post-footer">
<div class="post-tags">
<a href="/tags/JVM/" rel="tag"># JVM</a>
</div>
<div class="post-nav">
<div class="post-nav-item">
<a href="/e94a0a.html" rel="prev" title="Redis 应用">
<i class="fa fa-angle-left"></i> Redis 应用
</a>
</div>
<div class="post-nav-item">
<a href="/dcdf6e0b.html" rel="next" title="JVM 与类加载">
JVM 与类加载 <i class="fa fa-angle-right"></i>
</a>
</div>
</div>
</footer>
</article>
</div>
</div>
</main>
<footer class="footer">
<div class="footer-inner">
<div class="copyright">
©
<span itemprop="copyrightYear">2025</span>
<span class="with-love">
<i class="fa fa-heart"></i>
</span>
<span class="author" itemprop="copyrightHolder">tallate</span>
</div>
<div class="powered-by">由 <a href="https://hexo.io/" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/" rel="noopener" target="_blank">NexT.Gemini</a> 强力驱动
</div>
</div>
</footer>
<div class="back-to-top" role="button" aria-label="返回顶部">
<i class="fa fa-arrow-up fa-lg"></i>
<span>0%</span>
</div>
<a href="https://github.com/tallate" class="github-corner" title="在 GitHub 上关注我" aria-label="在 GitHub 上关注我" rel="noopener" target="_blank"><svg width="80" height="80" viewBox="0 0 250 250" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a>
<noscript>
<div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>
<script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/next-boot.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/hexo-generator-searchdb/1.4.1/search.js" integrity="sha256-1kfA5uHPf65M5cphT2dvymhkuyHPQp5A53EGZOnOLmc=" crossorigin="anonymous"></script>
<script src="/js/third-party/search/local-search.js"></script>
<script class="next-config" data-name="mermaid" type="application/json">{"enable":true,"version":"7.1.2","options":null,"js":{"url":"https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.3.0/mermaid.min.js","integrity":"sha256-9y71g5Lz/KLsHjB8uXwnkuWDtAMDSzD/HdIbqhJfTAI="}}</script>
<script src="/js/third-party/tags/mermaid.js"></script>
</body>
</html>