Preface 本 书 涵 盖 了 Java Native Interface(JNI) 的 内 容, 将 探 讨 以 下 问 题 : 在 一 个 Java 项 目 中 集 成 一 个 C/C++ 库 在 一 个 用 C/C++ 开 发 的 项 目 中, 嵌 入 JavaVM 实 现 Java VM



Similar documents
《C语言基础入门》课程教学大纲

I

<433A5C446F63756D656E E E67735C41646D696E F725CD7C0C3E65CC2DBCEC4CFB5CDB3CAB9D3C3D6B8C4CFA3A8BCF2BBAFA3A95CCAB9D3C3D6B8C4CF31302D31392E646F63>

何 秋 琳 张 立 春 视 觉 学 习 研 究 进 展 视 觉 注 意 视 觉 感 知

深圳市新亚电子制程股份有限公司

说 明 为 了 反 映 教 运 行 的 基 本 状 态, 为 校 和 院 制 定 相 关 政 策 和 进 行 教 建 设 与 改 革 提 供 据 依 据, 校 从 程 资 源 ( 开 类 别 开 量 规 模 ) 教 师 结 构 程 考 核 等 维 度, 对 2015 年 春 季 期 教 运 行 基

第2章 数据类型、常量与变量

评 委 : 李 炎 斌 - 个 人 技 术 标 资 信 标 初 步 审 查 明 细 表 序 号 投 标 单 位 投 标 函 未 按 招 标 文 件 规 定 填 写 漏 填 或 内 容 填 写 错 误 的 ; 不 同 投 标 人 的 投 标 文 件 由 同 一 台 电 脑 或 同 一 家 投 标 单

一 公 共 卫 生 硕 士 专 业 学 位 论 文 的 概 述 学 位 论 文 是 对 研 究 生 进 行 科 学 研 究 或 承 担 专 门 技 术 工 作 的 全 面 训 练, 是 培 养 研 究 生 创 新 能 力, 综 合 运 用 所 学 知 识 发 现 问 题, 分 析 问 题 和 解 决

龚 亚 夫 在 重 新 思 考 基 础 教 育 英 语 教 学 的 理 念 一 文 中 援 引 的 观 点 认 为 当 跳 出 本 族 语 主 义 的 思 维 定 式 后 需 要 重 新 思 考 许 多 相 连 带 的 问 题 比 如 许 多 发 音 的 细 微 区 别 并 不 影 响 理 解 和

,,,,, :,, (.,, );, (, : ), (.., ;. &., ;.. &.., ;, ;, ),,,,,,, ( ) ( ),,,,.,,,,,, : ;, ;,.,,,,, (., : - ),,,, ( ),,,, (, : ),, :,

修改版-操作手册.doc


18 上 报 该 学 期 新 生 数 据 至 阳 光 平 台 第 一 学 期 第 四 周 至 第 六 周 19 督 促 学 习 中 心 提 交 新 增 专 业 申 请 第 一 学 期 第 四 周 至 第 八 周 20 编 制 全 国 网 络 统 考 十 二 月 批 次 考 前 模 拟 题 第 一 学

评 委 : 徐 岩 宇 - 个 人 技 术 标 资 信 标 初 步 审 查 明 细 表 序 号 投 标 单 位 投 标 函 未 按 招 标 文 件 规 定 填 写 漏 填 或 内 容 填 写 错 误 的 ; 不 同 投 标 人 的 投 标 文 件 由 同 一 台 电 脑 或 同 一 家 投 标 单

( ) 信 号 与 系 统 Ⅰ 学 科 基 础 必 修 课 教 周 2016 年 06 月 13 日 (08:00-09:35) ( )

珠江钢琴股东大会


Microsoft Word - 第7章 图表反转形态.doc

国债回购交易业务指引

采 取 行 动 的 机 会 90% 开 拓 成 功 的 道 路 2

0 年 上 半 年 评 价 与 考 核 细 则 序 号 部 门 要 素 值 考 核 内 容 考 核 方 式 考 核 标 准 考 核 ( 扣 原 因 ) 考 评 得 3 安 全 生 产 目 30 无 同 等 责 任 以 上 道 路 交 通 亡 人 事 故 无 轻 伤 责 任 事 故 无 重 大 质 量

2 熟 悉 Visual Basic 的 集 成 开 发 环 境 3 了 解 可 视 化 面 向 对 象 编 程 事 件 驱 动 交 互 式 开 发 等 基 本 概 念 4 了 解 Visual Basic 的 特 点 环 境 要 求 与 安 装 方 法 1 Visual Basic 开 发 应 用

全国建筑市场注册执业人员不良行为记录认定标准(试行).doc


证券代码: 证券简称:长城电脑 公告编号:

2006年顺德区高中阶段学校招生录取分数线

<4D F736F F D D323630D6D0B9FAD3A6B6D4C6F8BAF2B1E4BBAFB5C4D5FEB2DFD3EBD0D0B6AF C4EAB6C8B1A8B8E6>

<4D F736F F D20B9D8D3DAB0BABBAAA3A8C9CFBAA3A3A9D7D4B6AFBBAFB9A4B3CCB9C9B7DDD3D0CFDEB9ABCBBE C4EAC4EAB6C8B9C9B6ABB4F3BBE1B7A8C2C9D2E2BCFBCAE92E646F6378>

上海证券交易所会议纪要

教师上报成绩流程图

HSK( 一 级 ) 考 查 考 生 的 日 常 汉 语 应 用 能 力, 它 对 应 于 国 际 汉 语 能 力 标 准 一 级 欧 洲 语 言 共 同 参 考 框 架 (CEF) A1 级 通 过 HSK( 一 级 ) 的 考 生 可 以 理 解 并 使 用 一 些 非 常 简 单 的 汉 语

现 场 会 议 时 间 为 :2016 年 5 月 19 日 网 络 投 票 时 间 为 :2016 年 5 月 18 日 年 5 月 19 日 其 中 通 过 深 圳 证 券 交 易 所 交 易 系 统 进 行 网 络 投 票 的 时 间 为 2016 年 5 月 19 日 9:30-

ETF、分级基金规模、份额变化统计


 编号:

3 月 30 日 在 中 国 证 券 报 上 海 证 券 报 证 券 时 报 证 券 日 报 和 上 海 证 券 交 易 所 网 站 上 发 出 召 开 本 次 股 东 大 会 公 告, 该 公 告 中 载 明 了 召 开 股 东 大 会 的 日 期 网 络 投 票 的 方 式 时 间 以 及 审

马 克 思 主 义 公 正 观 的 基 本 向 度 及 方 法 论 原 则!! # #

Microsoft Word - 文件汇编.doc

第二讲 数列

金 不 少 于 800 万 元, 净 资 产 不 少 于 960 万 元 ; (3) 近 五 年 独 立 承 担 过 单 项 合 同 额 不 少 于 1000 万 元 的 智 能 化 工 程 ( 设 计 或 施 工 或 设 计 施 工 一 体 ) 不 少 于 2 项 ; (4) 近 三 年 每 年

<4D F736F F D C3E6CFF2B6D4CFF3A3A8B5DAC8FDD5C220C0E0CCD8D0D4A3A92E646F63>

登录、注册功能的测试用例设计.doc

目 录 一 系 统 访 问... 1 二 门 户 首 页 申 报 用 户 审 核 用 户... 2 三 系 统 登 录 用 户 名 密 码 登 录 新 用 户 注 册 用 户 登 录 已 注 册 用

<4D F736F F D20B6C0C1A2B6ADCAC2D0ECCCFABEFDCFC8C9FABCB0CCE1C3FBC8CBC9F9C3F72E646F63>

附 件 : 上 海 市 建 筑 施 工 企 业 施 工 现 场 项 目 管 理 机 构 关 键 岗 位 人 员 配 备 指 南 二 一 四 年 九 月 十 一 日 2

( 二 ) 现 行 统 一 高 考 制 度 不 利 于 培 养 人 的 创 新 精 神,,,,,,,,,,,,, [ ],,,,,,,,,,, :, ;,,,,,,? ( 三 ) 现 行 统 一 高 考 制 度 不 利 于 全 体 学 生 都 获 得 全 面 发 展,, [ ],,,,,,,,,,,

抗 战 时 期 国 民 政 府 的 银 行 监 理 体 制 探 析 % # % % % ) % % # # + #, ) +, % % % % % % % %

<433A5C C6B73625C B746F705CB9FABCCAD6D0D2BDD2A9D7A8D2B5B8DFBCB6BCBCCAF5D6B0B3C6C6C0C9F3C9EAC7EBD6B8C4CFA3A CDA8D3C3B0E6A3A92E646F63>

3 复 试 如 何 准 备 4 复 试 成 绩 计 算 5 复 试 比 例 6 复 试 类 型 7 怎 么 样 面 对 各 种 复 试 04 05

一、资质申请

名 称 生 命 科 学 学 院 环 境 科 学 1 生 物 学 仅 接 收 院 内 调 剂, 初 试 分 数 满 足 我 院 生 物 学 复 试 最 低 分 数 线 生 命 科 学 学 院 生 态 学 5 生 态 学 或 生 物 学 生 命 科 学 学 院

课程类 别

一 从 分 封 制 到 郡 县 制 一 从 打 虎 亭 汉 墓 说 起

类 似 地, 又 可 定 义 变 下 限 的 定 积 分 : ( ). 与 ψ 统 称 为 变 限 积 分. f ( ) d f ( t) dt,, 注 在 变 限 积 分 (1) 与 () 中, 不 可 再 把 积 分 变 量 写 成 的 形 式 ( 例 如 ) 以 免 与 积 分 上 下 限 的

第 四 条 建 设 单 位 对 可 能 产 生 职 业 病 危 害 的 建 设 项 目, 应 当 依 照 本 办 法 向 安 全 生 产 监 督 管 理 部 门 申 请 职 业 卫 生 三 同 时 的 备 案 审 核 审 查 和 竣 工 验 收 建 设 项 目 职 业 卫 生 三 同 时 工 作 可

电信系教学大纲的基本规范

光明乳业股份有限公司

Template BR_Rec_2005.dot

抗 日 战 争 研 究 年 第 期

目 录 关 于 图 标... 3 登 陆 主 界 面... 3 工 单 管 理... 5 工 单 列 表... 5 搜 索 工 单... 5 工 单 详 情... 6 创 建 工 单... 9 设 备 管 理 巡 检 计 划 查 询 详 情 销 售 管

工 程 勘 察 资 质 标 准 根 据 建 设 工 程 勘 察 设 计 管 理 条 例 和 建 设 工 程 勘 察 设 计 资 质 管 理 规 定, 制 定 本 标 准 一 总 则 ( 一 ) 本 标 准 包 括 工 程 勘 察 相 应 专 业 类 型 主 要 专 业 技 术 人 员 配 备 技 术

黄 金 原 油 总 持 仓 增 长, 同 比 增 幅 分 别 为 4.2% 和 4.1% 而 铜 白 银 以 及 玉 米 则 出 现 减 持, 减 持 同 比 减 少 分 别 为 9.4%,9.4% 以 及 6.5% 大 豆, 豆 粕 结 束 连 续 4 周 总 持 仓 量 增 长, 出 现 小 幅

中 中 中 中 部 中 岗 位 条 件 历 其 它 历 史 师 地 理 师 生 物 师 体 与 健 康 师 从 事 中 历 史 工 从 事 中 地 理 工 从 事 中 生 物 工 从 事 中 体 与 健 康 工 2. 课 程 与 论 ( 历 史 ); 2. 科 ( 历 史 )

伊 犁 师 范 学 院 611 语 言 学 概 论 全 套 考 研 资 料 <2016 年 最 新 考 研 资 料 > 2-2 语 言 学 纲 要 笔 记, 由 考 取 本 校 本 专 业 高 分 研 究 生 总 结 而 来, 重 点 突 出, 借 助 此 笔 记 可 以 大 大 提 高 复 习 效

定 位 和 描 述 : 程 序 设 计 / 办 公 软 件 高 级 应 用 级 考 核 内 容 包 括 计 算 机 语 言 与 基 础 程 序 设 计 能 力, 要 求 参 试 者 掌 握 一 门 计 算 机 语 言, 可 选 类 别 有 高 级 语 言 程 序 设 计 类 数 据 库 编 程 类

正 规 培 训 达 规 定 标 准 学 时 数, 并 取 得 结 业 证 书 二 级 可 编 程 师 ( 具 备 以 下 条 件 之 一 者 ) (1) 连 续 从 事 本 职 业 工 作 13 年 以 上 (2) 取 得 本 职 业 三 级 职 业 资 格 证 书 后, 连 续 从 事 本 职 业

导 数 和 微 分 的 概 念 导 数 的 几 何 意 义 和 物 理 意 义 函 数 的 可 导 性 与 连 续 性 之 间 的 关 系 平 面 曲 线 的 切 线 和 法 线 导 数 和 微 分 的 四 则 运 算 基 本 初 等 函 数 的 导 数 复 合 函 数 反 函 数 隐 函 数 以

西 南 民 族 学 院 学 报 哲 学 社 会 科 学 版 第 卷 资 料 来 源 中 国 统 计 年 鉴 年 年 新 中 国 五 十 年 统 计 资 料 汇 编 中 国 人 口 统 计 年 鉴 年 数 据 资 料 来 源 中 国 统 计 年 鉴 中 国 统 计 出 版 社 年 版 资 料 来 源

Microsoft Word - 中节能_工业项目节能评估审查导则Draft.doc

2014年中央财经大学研究生招生录取工作简报

第 六 章 债 券 股 票 价 值 评 估 1 考 点 一 : 债 券 价 值 的 影 响 因 素 2

新, 各 地 各 部 门 ( 单 位 ) 各 文 化 事 业 单 位 要 高 度 重 视, 切 实 加 强 领 导, 精 心 组 织 实 施 要 根 据 事 业 单 位 岗 位 设 置 管 理 的 规 定 和 要 求, 在 深 入 调 查 研 究 广 泛 听 取 意 见 的 基 础 上, 研 究 提

北京信息科技大学本科学生成绩管理办法


一 开 放 性 的 政 策 与 法 规 二 两 岸 共 同 的 文 化 传 承 三 两 岸 高 校 各 自 具 有 专 业 优 势 远 见 杂 志 年 月 日

¹ º ¹ º 农 业 流 动 人 口 是 指 户 口 性 质 为 农 业 户 口 在 流 入 地 城 市 工 作 生 活 居 住 一 个 月 及 以 上 的 流 动 人 口 非 农 流 动 人 口 是 指 户 口 性 质 为 非 农 户 口 在 流 入 地 城 市 工 作 生 活 居 住 一 个

i 1) 系 统 运 作 前 设 定 *1. [2.1 网 页 主 机 名 称 设 定 ] -- 设 定 校 务 系 统 的 主 机 IP 地 址, 以 供 其 他 个 人 电 脑 连 接 及 使 用 该 系 统 *2. [2.3.1 输 入 / 修 改 学 校 资 料 ] -- 输 入 系 统 使

<4D F736F F D20BFC9B1E0B3CCD0F2BFD8D6C6CFB5CDB3C9E8BCC6CAA6B9FABCD2D6B0D2B5B1EAD7BC2E646F63>

随着执业中医师资格考试制度的不断完善,本着为我校中医学专业认证服务的目的,本文通过对我校中医类毕业生参加2012年和2013年的中医执业医师考试成绩及通过率、掌握率进行分析,并与全国的平均水平进行差异比较分析,以此了解我校执业中医师考试的现状,进而反映我校中医类课程总体教学水平,发现考核知识模块教学中存在的不足,反馈给相关学院和教学管理部门,以此提高教学和管理水平。

白皮书

年 8 月 11 日, 公 司 召 开 2015 年 第 五 次 临 时 股 东 大 会, 审 议 通 过 了 关 于 公 司 <2015 年 股 票 期 权 激 励 计 划 ( 草 案 )> 及 其 摘 要 的 议 案 关 于 提 请 股 东 大 会 授 权 董 事 会 办 理 公

B-002 行 政 处 罚 在 气 象 探 测 环 境 保 护 范 围 内 从 事 危 害 气 象 探 测 环 境 活 动 的 处 罚 中 华 人 民 共 和 国 气 象 法 第 三 十 五 条 第 一 款 第 二 项 B-003 行 政 处 罚 在

·岗位设置管理流程

收 入 支 出 项 目 2016 年 预 算 项 目 2016 年 预 算 预 算 01 表 单 位 : 万 元 ( 保 留 两 位 小 数 ) 一 公 共 财 政 预 算 拨 款 一 人 员 经 费 一 般 财 力 人 员 支 出 成 品

合 并 计 算 配 售 对 象 持 有 多 个 证 券 账 户 的, 多 个 证 券 账 户 市 值 合 并 计 算 确 认 多 个 证 券 账 户 为 同 一 配 售 对 象 持 有 的 原 则 为 证 券 账 户 注 册 资 料 中 的 账 户 持 有 人 名 称 有 效 身 份 证 明 文 件

投 资 者 可 在 基 金 管 理 人 指 定 的 销 售 机 构 申 购 和 赎 回 美 元 等 外 币 销 售 的 基 金 份 额, 具 体 详 见 基 金 管 理 人 相 关 公 告 2 在 三 申 购 与 赎 回 的 原 则 部 分 增 加 : 1 本 基 金 采 用 多 币 种 销 售,

用节点法和网孔法进行电路分析

!!!!!

2. 本 次 修 改 后, 投 资 者 申 购 新 股 的 持 有 市 值 要 求 市 值 计 算 规 则 及 证 券 账 户 使 用 的 相 关 规 定 是 否 发 生 了 变 化? 答 : 未 发 生 变 化 投 资 者 申 购 新 股 的 持 有 市 值 是 指, 以 投 资 者 为 单 位

三门峡市质量技术监督局清单公示

自 服 务 按 钮 无 法 访 问 新 系 统 的 自 服 务 页 面 因 此 建 议 用 户 从 信 网 中 心 ( 主 页, 右 下 角 位 置 的 常 用 下 载, 或 校 园 网 用 户 自 服 务 ( 首 页

境 外 上 市 外 资 股 股 东 持 有 股 份 总 数 (H 股 ) 489,157,907 3 出 席 会 议 的 股 东 所 持 有 表 决 权 股 份 数 占 公 司 有 表 决 权 股 份 总 数 的 其 中 :A 股 股 东 持 股 占 股 份 总 数 的

<4D F736F F D20B3D6B2D6CFDEB6EEB1EDB8F1D7EED6D52E646F63>

全国教师资格认定管理信息系统

<4D F736F F D20D6D8D3CA3535BAC5B9D8D3DAD3A1B7A2A1B6D6D8C7ECD3CAB5E7B4F3D1A7D1A7CABFD1A7CEBBCADAD3E8B9A4D7F7CFB8D4F2A1B7B5C4CDA8D6AA2E646F63>

<4D F736F F D20B8BDBCFE34A3BAD2A9C6B7B2B9B3E4C9EAC7EBD7A2B2E1CAC2CFEEBCB0C9EAB1A8D7CAC1CFD2AAC7F32E646F63>

2.5 选 举 陈 晓 非 女 士 为 第 六 届 董 事 会 董 事 候 选 人 的 议 案 ; 2.6 选 举 卢 婕 女 士 为 第 六 届 董 事 会 董 事 候 选 人 的 议 案 ; 2.7 选 举 张 文 君 先 生 为 第 六 届 董 事 会 独 立 董 事 候 选 人 的 议 案

工 程 造 价 咨 询 企 业 管 理 系 统 操 作 手 册 目 录 1 造 价 企 业 登 录 企 业 基 本 信 息 查 看 企 业 人 员 信 息 查 看 企 业 基 本 信 息 操 作 企 业 简 介 企 业 章

股票代码:000936

Transcription:

前 言 翻 译 初 衷, 记 录 JNI 编 程 经 验 以 备 后 查, 并 奢 望 以 JNI 为 蓝 本, 写 一 本 更 深 入 的 关 于 虚 拟 机 的 书 真 做 起 来, 才 发 现 以 现 有 水 平 只 能 仰 望 这 个 目 标, 要 达 到 它, 还 需 要 几 年 积 累 本 书 没 有 采 用 逐 字 逐 句 的 翻 译, 更 多 采 用 意 译, 请 大 家 在 阅 读 时 多 参 考 原 著 ; 对 于 书 中 夹 杂 的 评 论, 如 有 伤 观 感, 请 大 家 见 谅 现 在 有 无 数 优 秀 的 开 源 项 目, 以 前 高 深 莫 测 的 技 术 ( 虚 拟 机 编 译 器 操 作 系 统 协 议 栈 和 IDE...), 我 们 终 于 有 机 会 一 探 究 竟 了, 真 令 人 兴 奋 我 们 学 习, 我 们 参 与, 希 望 有 一 天 我 们 中 国 人 也 能 创 一 门 牛 技 术 感 谢 Die...ken 的 审 稿, 他 严 谨 和 认 真 的 态 度, 深 感 敬 佩 ; 哥 们 儿 祝 你 : 天 天 开 心, 早 结 连 理 感 谢 老 婆 老 婆 读 书 时, 看 见 别 人 写 的 书 总 会 感 谢 太 太 云 云, 煞 是 羡 慕, 总 追 问 : 你 什 么 时 候 写 书 感 谢 我? 难! 翻 译 都 这 么 费 劲, 写 书 就 不 知 猴 年 马 月 了, 在 这 儿 感 谢 一 下, 糊 弄 糊 弄 得 了 do.chuan@gmail.com

Preface 本 书 涵 盖 了 Java Native Interface(JNI) 的 内 容, 将 探 讨 以 下 问 题 : 在 一 个 Java 项 目 中 集 成 一 个 C/C++ 库 在 一 个 用 C/C++ 开 发 的 项 目 中, 嵌 入 JavaVM 实 现 Java VM 语 言 互 操 作 性 问 题, 特 别 是 互 操 作 过 程 中 的 垃 圾 回 收 (GC, garbage collection) 和 并 发 编 程 (multithreading) 译 注 : JNI(Java Native Interface) 是 SUN 定 义 的 一 套 标 准 接 口, 如 Dalvik, Apache Harmony 项 目... 等 Java 虚 拟 机, 都 会 实 现 JNI 接 口, 供 本 地 (C/C++) 应 用 与 Java VM 互 调 JNI: 可 以 供 Java 代 码 调 用 本 地 代 码, 本 地 代 码 也 可 以 调 用 Java 代 码, 即 上 文 列 出 的 第 4 条 内 容 : 语 言 互 操 作 ; 所 以, 这 是 一 套 完 善 而 功 能 强 大 的 接 口 可 能 有 朋 友 听 说 过 KNI, 那 是 J2ME VM(CLDC) 中 定 义 的 一 套 东 西, 不 如 JNI 强 大 此 外, 因 为 C/C++ 在 系 统 编 程 领 域 的 地 位, 只 要 打 通 了 与 C/C++ 的 接 口, 就 等 于 是 天 堑 变 通 途 首 先, 通 过 本 书, 你 会 很 容 易 的 掌 握 JNI 开 发, 并 能 了 解 到 方 方 面 面 的 关 于 JNI 的 知 识 本 书 详 尽 的 叙 述, 会 带 给 你 你 很 多 如 何 高 效 使 用 JNI 的 启 示 JNI 自 1997 年 初 发 布 以 来, Sun 的 工 程 师 们 和 Java 社 区 使 用 JNI 的 经 验 造 就 了 本 书 第 二, 本 书 介 绍 了 JNI 的 设 计 原 理 这 些 原 理, 不 仅 会 使 学 术 界 感 兴 趣, 也 是 高 效 使 用 JNI 的 前 提 第 三, 本 书 的 某 些 部 分 是 Java 2 平 台 规 范 的 最 终 版 本 JNI 程 序 员 可 以 此 书 作 为 规 范 的 参 考 手 册,Java 虚 拟 机 实 现 者 必 须 遵 循 规 范, 以 保 证 各 平 台 实 现 的 一 致 性 (... 几 段 不 重 要, 未 翻 译...)

CHAPTER 1 Introduction JNI 是 Java 平 台 中 的 一 个 强 大 特 性 应 用 程 序 可 以 通 过 JNI 把 C/C++ 代 码 集 成 进 Java 程 序 中 通 过 JNI, 开 发 者 在 利 用 Java 平 台 强 大 功 能 的 同 时, 又 不 必 放 弃 对 原 有 代 码 的 投 资 ; 因 为 JNI 是 Java 平 台 定 义 的 规 范 接 口, 当 程 序 员 向 Java 代 码 集 成 本 地 库 时, 只 要 在 一 个 平 台 中 解 决 了 语 言 互 操 作 问 题, 就 可 以 把 该 解 决 方 案 比 较 容 易 的 移 植 到 其 他 Java 平 台 中 译 注 : 比 如 为 Dalvik 添 加 了 一 个 本 地 库, 也 可 以 把 这 个 本 地 库 很 容 易 的 移 植 到 J2SE 和 Apache Harmony 中, 因 为 在 Java 与 C/C++ 互 操 作 方 面, 大 家 都 遵 循 一 套 API 接 口, 即 JNI 本 书 由 下 列 三 个 部 分 组 成 : Chapter 2 通 过 简 单 示 例 介 绍 了 JNI 编 程 Chapter 3-10, 对 JNI 各 方 面 特 性 和 功 能 做 介 绍, 并 给 出 示 例 ( 译 者 : 重 要 ) Chapters 11-13, 罗 列 JNI 所 有 的 数 据 类 型 的 定 义 (... 几 段 不 重 要, 未 翻 译...) 1.1 The Java Platform and Host Environment 因 本 书 覆 盖 了 Java 和 本 地 (C, C++, etc...) 编 程 语 言, 让 我 们 首 先 理 一 理 这 些 编 程 语 言 的 适 用 领 域 Java 平 台 (Java Platform) 的 组 成 :Java VM 和 Java API. Java 应 用 程 序 使 用 Java 语 言 开 发, 然 后 编 译 成 与 平 台 无 关 的 字 节 码 (.class 文 件 ) Java API 由 一 组 预 定 义 的 类 组 成 任 何 组 织 实 现 的 Java 平 台 都 要 支 持 :Java 编 程 语 言, 虚 拟 机, 和 API( 译 者 :Sun 对 Java 语 言 虚 拟 机 和 API 有 明 确 规 范 ) 平 台 环 境 : 操 作 系 统, 一 组 本 机 库, 和 CPU 指 令 集 本 地 应 用 程 序, 通 常 依 赖 于 一 个 特 定 的 平 台 环 境, 用 C C++ 等 语 言 开 发, 并 被 编 译 成 平 台 相 关 的 二 进 制 指 令, 目 标 二 进 制 代 码 在 不 同 OS 间 一 般 不 具 有 可 移 植 性

Java 平 台 (Java VM 和 Java API) 一 般 在 某 个 平 台 下 开 发 比 如,Sun 的 Java Runtime Environment(JRE) 支 持 类 Unix 和 Windows 平 台. Java 平 台 做 的 所 有 努 力, 都 为 了 使 程 序 更 具 可 移 植 性 1.2 Role of the JNI 当 Java 平 台 部 署 到 本 地 系 统 中, 有 必 要 做 到 让 Java 程 序 与 本 地 代 码 协 同 工 作 部 分 是 由 于 遗 留 代 码 ( 保 护 原 有 的 投 资 ) 的 问 题 ( 一 些 效 率 敏 感 的 代 码 用 C 实 现, 但 现 在 JavaVM 的 执 行 效 率 完 全 可 信 赖 ), 工 程 师 们 很 早 就 开 始 以 C/C++ 为 基 础 构 建 Java 应 用, 所 以,C/C++ 代 码 将 长 时 间 的 与 Java 应 用 共 存 JNI 让 你 在 利 用 强 大 Java 平 台 的 同 时, 使 你 仍 然 可 以 用 其 他 语 言 写 程 序 作 为 JavaVM 的 一 部 分,JNI 是 一 套 双 向 的 接 口, 允 许 Java 与 本 地 代 码 间 的 互 操 作 如 图 1.1 所 示 作 为 双 向 接 口,JNI 支 持 两 种 类 型 本 地 代 码 : 本 地 库 和 本 地 应 用 用 本 地 代 码 实 现 Java 中 定 义 的 native method 接 口, 使 Java 调 用 本 地 代 码 通 过 JNI 你 可 以 把 Java VM 嵌 到 一 个 应 用 程 序 中, 此 时 Java 平 台 作 为 应 用 程 序 的 增 强, 使 其 可 以 调 用 Java 类 库 比 如, 在 浏 览 器 中 运 行 Applet, 当 浏 览 器 遇 到 "Applet" 标 签, 浏 览 器 就 会 把 标 签 中 的 内 容 交 给 Java VM 解 释 执 行, 这 个 实 现, 就 是 典 型 的 把 JavaVM 嵌 入 Browser 中 译 注 : JNI 不 只 是 一 套 接 口, 还 是 一 套 使 用 规 则 Java 语 言 有 "native" 关 键 字, 声 明 哪 些 方 法

是 用 本 地 代 码 实 现 的. 翻 译 的 时 候, 对 于 "native method", 根 据 上 下 文 意 思 做 了 不 同 处 理, 当 native method 指 代 Java 中 用 "native" 关 键 字 修 饰 的 那 些 方 法 时, 不 翻 译 ; 而 当 代 码 用 C/C++ 实 现 的 部 分 翻 译 成 了 本 地 代 码 上 述, 在 应 用 中 嵌 入 Java VM 的 方 法, 是 用 最 少 的 力 量, 为 应 用 做 最 强 扩 展 的 不 二 选 择, 这 时 你 的 应 用 程 序 可 以 自 由 使 用 Java API 的 所 有 功 能 ; 大 家 有 兴 趣 可 以 读 一 读 浏 览 器 是 怎 么 扩 展 Applet 的, 或 者 读 一 读 Palm WebOS 的 东 西 译 者 最 近 一 年 都 在 做 这 件 事, 对 这 个 强 大 的 功 能, 印 象 特 别 深 刻. 我 们 整 个 小 组 做 了 两 个 平 台 的 扩 展, 设 计 编 码 测 试 和 debug 用 了 近 一 年 半 时 间, 代 码 量 在 14000 行 左 右, 做 完 扩 展 后, 平 台 功 能 空 前 增 强 我 感 觉 做 软 件, 难 得 不 在 编 码, 难 在 开 始 的 设 计 和 后 期 的 测 试 调 试 和 优 化, 并 最 终 商 用, 这 就 要 求 最 终 产 品 是 一 个 强 大 而 稳 定 的 平 台, 达 到 此 目 标 是 个 旷 日 持 久 的 事. 看 看 Java,Windows,Linux,Qt,WebKit 发 展 了 多 少 年? 向 所 有 软 件 工 程 师 致 敬! 1.3 Implications of Using the JNI 请 记 住, 当 Java 程 序 集 成 了 本 地 代 码, 它 将 丢 掉 Java 的 一 些 好 处 首 先, 脱 离 Java 后, 可 移 植 性 问 题 你 要 自 己 解 决, 且 需 重 新 在 其 他 平 台 编 译 链 接 本 地 库 第 二, 要 小 心 处 理 JNI 编 程 中 各 方 面 问 题 和 来 自 C/C++ 语 言 本 身 的 细 节 性 问 题, 处 理 不 当, 应 用 将 崩 溃 一 般 性 原 则 : 做 好 应 用 程 序 架 构, 使 native methods 定 义 在 尽 可 能 少 的 几 个 类 里 译 注 : 学 习 JNI 编 程 是 个 漫 长 的 实 践 过 程, 会 碰 到 无 数 问 题 用 C/C++ 编 程, 常 见 问 题 有 内 存 泄 露, 指 针 越 界..., 此 外 使 用 了 JNI, 还 要 面 对 JavaVM 的 问 题 : 在 本 地 代 码 中 new 一 个 Java 对 象 后 期 望 在 本 地 代 码 中 维 护 此 对 象 的 引 用, 如 何 避 免 被 GC? Java 面 向 对 象 语 言 的 封 装 性 被 破 坏 了,Java 类 中 任 何 方 法 和 属 性 对 JNI 都 是 可 见 的, 不 管 它 是 public 的, 还 是 private/protected/package 的 对 LocalRef/GlobalRef 管 理 不 善, 会 引 发 Table Overflow Exception, 导 致 应 用 崩 溃 从 JNI 调 用 Java 的 过 程 不 是 很 直 观, 往 往 几 行 Java 代 码 能 搞 定 的 事 情, 用 JNI 实 现 却 要 几 百 行 虽 然, 有 这 样 多 问 题, 逃 避 不 了, 你 就 认 了 吧 经 过 一 段 时 间 的 实 践, 当 你 能 熟 练 处 理 这 些 问 题 时, 就 会, 眉 头 一 皱, 文 思 泉 涌, 指 尖 飞 舞, 瞬 间 几 百 行 代 码 诞 生 了, 一 个 make 全 部 编 译 通 过, 这 时 的 你 肯 定 已 经 对 JNI 上 瘾 了... 1.4 When to Use the JNI 当 你 准 备 在 项 目 中 使 用 JNI 之 前, 请 先 考 虑 一 下 是 否 有 其 他 更 合 适 的 方 案 上 节 有 关 JNI 缺 点 的 介 绍, 应 该 引 起 你 足 够 的 重 视 这 里 介 绍 几 个 不 通 过 JNI 与 其 他 语 言 交 互 的 技 术 :

IPC 或 者 通 过 TCP/IP 网 络 方 案 ( Android ASE) 数 据 库 方 面, 可 以 使 用 JDBC 使 用 Java 的 分 布 式 对 象 技 术 : Java IDL API 译 注 : IPC 与 TCP/IP 是 常 用 的 基 于 协 议 的 信 息 交 换 方 案. 可 以 参 考 Android 上 的 Binder 和 ASE(Android Script Environment) 一 典 型 的 解 决 方 案 是,Java 程 序 与 本 地 代 码 分 别 运 行 在 不 同 的 进 程 中. 采 用 进 程 分 置 最 大 的 好 处 是 : 一 个 进 程 的 崩 溃, 不 会 立 即 影 响 到 另 一 个 进 程 但 是, 把 Java 代 码 与 本 地 代 码 置 于 一 个 进 程 有 时 是 必 要 的 如 下 : Java API 可 能 不 支 某 些 平 台 相 关 的 功 能 比 如, 应 用 程 序 执 行 中 要 使 用 Java API 不 支 持 的 文 件 类 型, 而 如 果 使 用 跨 进 程 操 作 方 式, 即 繁 琐 又 低 效 避 免 进 程 间 低 效 的 数 据 拷 贝 操 作 多 进 程 的 派 生 : 耗 时 耗 资 源 ( 内 存 ) 用 本 地 代 码 或 汇 编 代 码 重 写 Java 中 低 效 方 法 总 之, 如 果 Java 必 须 与 驻 留 同 进 程 的 本 地 代 码 交 互, 请 使 用 JNI 译 注 : 写 代 码 是 技 巧 和 艺 术, 看 你 想 在 设 计 上 下 多 大 功 夫. 比 如 : Chrome, 是 多 进 程 的 典 范, 她 的 简 洁 高 效, 令 人 叹 服 1.5 Evolution of the JNI 关 于 Java 应 用 程 序 如 何 与 本 地 代 码 互 操 作 的 问 题, 在 Java 平 台 早 期 就 被 提 了 出 来. JDK1.0 包 括 了 一 套 与 本 地 代 码 交 互 的 接 口 当 时 许 多 Java 方 法 和 库 都 依 赖 本 地 方 法 实 现 ( 如 java.io, java.net) 但 是,JDK release 1.0 有 两 个 主 要 问 题 : Java 虚 拟 机 规 范 未 定 义 对 象 布 局, 本 地 代 码 访 问 对 象 的 成 员 是 通 过 访 问 C 结 构 的 成 员 实 现 的 本 地 代 码 可 以 得 到 对 象 在 内 存 中 的 地 址, 所 以, 本 地 方 法 是 GC 相 关 的 为 解 决 上 述 问 题 对 JNI 做 了 重 新 设 计, 让 这 套 接 口 在 所 有 平 台 都 容 易 得 到 支 持 虚 拟 机 实 现 者 通 过 JNI 支 持 大 量 的 本 地 代 码 工 具 开 发 商 不 用 处 理 不 同 种 类 的 本 地 接 口 所 有 JNI 开 发 者 面 对 的 是 操 作 JavaVM 的 规 范 API

JNI 的 首 次 支 持 是 在 JDK release 1.1, 但 1.1 内 部 Java 与 本 地 代 码 的 交 互 仍 然 使 用 原 始 方 式 (JDK 1.0). 但 这 种 局 面, 没 有 持 续 很 久, 在 Java 2 SDK release 1.2 中 Java 层 与 本 地 代 码 的 交 互 部 分 用 JNI 重 写 了 作 为 JavaVM 规 范 的 一 部 分,Java 层 与 本 地 代 码 的 交 互, 都 应 通 过 JNI 实 现 1.6 Example Programs 本 书 注 重 JNI 编 程, 不 涉 及 如 何 通 过 第 三 方 工 具 简 化 该 过 程 ( 译 者 : 不 重 要, 未 翻 译 ) 请 从 官 网 下 载 本 书 的 示 例 代 码 :http://java.sun.com/docs/books/jni/

CHAPTER 2 Getting Started 本 章 用 Hello World 示 例 带 你 领 略 JNI 编 程 2.1 Overview 准 备 过 程 : 1. 创 建 一 个 类 (HelloWorld.java) 2. 使 用 javac 编 译 该 类 3. 利 用 javah -jni 产 生 头 文 件 4. 用 本 地 代 码 实 现 头 文 件 中 定 义 的 方 法 5. Run

译 注 : 在 一 个 特 定 环 境 中, 写 本 地 实 现 的 过 程 是 不 同 的 ( 如 Android) javah 主 要 是 生 成 头 文 件 和 函 数 签 名 ( 每 个 方 法 和 成 员 都 有 签 名, 后 有 详 细 介 绍 ), 通 过 javah 学 习 如 何 正 确 的 写 法 注 意 : 如 上 述 HelloWorld.java, 编 译 后 的 文 件 为 HelloWorld.class, 用 $javah HelloWorld 来 产 生 头 文 件, 不 要 带 末 尾 的 ".class" 2.2 Declare the Native Method HelloWorld.java class HelloWorld private native void print(); public static void main(string[] args) new HelloWorld().print(); static System.loadLibrary("HelloWorld"); HelloWrold 类 首 先 声 明 了 一 个 private native print 方 法. static 那 几 行 是 本 地 库 在 Java 代 码 中 声 明 本 地 方 法 必 须 有 "native" 标 识 符,native 修 饰 的 方 法, 在 Java 代 码 中 只 作 为 声 明 存 在 在 调 用 本 地 方 法 前, 必 须 首 先 装 载 含 有 该 方 法 的 本 地 库. 如 HelloWorld.java 中 所 示, 置 于 static 块 中, 在 Java VM 初 始 化 一 个 类 时, 首 先 执 行 这 部 分 代 码, 这 可 保 证 调 用 本 地 方 法 前, 装 载 了 本 地 库 装 载 库 的 机 制, 后 有 介 绍 2.3 Compile the HelloWorld Class $javac HelloWorld.java 2.4 Create the Native Method Header File $javah -jni HelloWorld 译 者 :"-jni" 为 默 认 参 数, 可 有 可 无. 上 述 命 令, 生 成 HelloWorld.h 文 件. 关 键 部 分 如 下 :

JNIEXPORT void JNICALL Java_HelloWorld_print (JNIEnv *, jobject); 现 在, 请 先 忽 略 两 个 宏 :JNIEXPORT 和 JNICALL 你 会 发 现, 该 函 数 声 明, 接 受 两 个 参 数, 而 对 应 的 Java 代 码 对 该 函 数 的 声 明 没 有 参 数 第 一 个 参 数 是 指 向 JNIEnv 结 构 的 指 针 ; 第 二 个 参 数, 为 HelloWorld 对 象 自 身, 即 this 指 针 译 注 : JNIEnv 是 JNI 核 心 数 据 之 一, 地 位 非 常 崇 高, 所 有 对 JNI 的 调 用 都 要 通 过 此 结 构 2.5 Write the Native Method Implementation 必 须 根 据 javah 生 成 的 本 地 函 数 声 明 实 现 函 数, 如 下 : #include <jni.h> #include <stdio.h> #include "HelloWorld.h" JNIEXPORT void JNICALL Java_HelloWorld_print(JNIEnv *env, jobject obj) printf("hello World!\n"); return; 请 注 意 :"jni.h" 文 件 必 须 被 包 含, 该 文 件 定 义 了 JNI 所 有 的 函 数 声 明 和 数 据 类 型 2.6 Compile the C Source and Create a Native Library 请 注 意, 生 成 的 本 地 库 的 名 字, 必 须 与 System.loadLibrary("HelloWorld"); 待 装 载 库 的 名 字 相 同 Solaris: $cc -G -I/java/include -I/java/include/solaris HelloWorld.c -o libhelloworld.so -G: 生 成 共 享 库 Win: $cl -Ic:\java\include -Ic:\java\include\win32 -MD -LD HelloWorld.c -FeHelloWorld.dll -MD: 保 证 与 Win32 多 线 程 C 库 连 接 ( 译 者 :Win 上 分 静 态 动 态 动 态 多 线 程...C 库 ) -LD: 生 成 动 态 链 接 库

2.7 Run the Program Solaris or Win: $java HelloWorld 输 出 : Hello World! 运 行 前, 必 须 保 证 连 接 器, 能 找 到 待 装 载 的 库, 不 然, 将 抛 如 下 异 常 : java.lang.unsatisfiedlinkerror: no HelloWorld in library path at java.lang.runtime.loadlibrary(runtime.java) at java.lang.system.loadlibrary(system.java) at HelloWorld.main(HelloWorld.java) 如,Solaris, 通 过 sh 或 ksh shell: $LD_LIBRARY_PATH=. $export LD_LIBRARY_PATH C shell: $setenv LD_LIBRARY_PATH. 在 Win 上, 请 保 证 待 装 载 库 在 当 前 位 置, 或 在 PATH 环 境 变 量 中 你 也 可 以 如 下 : java -Djava.library.path=. HelloWorld -D: 设 置 Java 平 台 的 系 统 属 性 此 时 JavaVM 可 以 在 当 前 位 置 找 到 该 库

CHAPTER 3 Basic Types, Strings, and Arrays JNI 编 程 中 常 被 提 到 的 问 题 是,Java 语 言 中 的 数 据 类 型 是 如 何 映 射 到 c/c++ 本 地 语 言 中 的 实 际 编 程 中, 向 函 数 传 参 和 函 数 返 回 值 是 很 普 遍 的 事 情 本 章 将 介 绍 这 方 面 技 术, 我 们 从 基 本 类 型 ( 如 int) 和 一 般 对 象 ( 如 String 和 Array) 开 始 介 绍. 其 他 内 容 将 放 在 下 一 章 介 绍 译 注 : JavaVM 规 范 中 称 int,char,byte 等 为 primitive types, 译 者 平 时 叫 惯 了 基 本 类 型, 所 以 翻 译 时 延 用 了 习 惯, 不 知 合 适 否 3.1 A Simple Native Method 扩 充 HelloWorld.java, 该 例 是 先 打 印 一 串 字 符, 然 后 等 待 用 户 的 输 入, 如 下 : class Prompt // native method that prints a prompt and reads a line private native String getline(string prompt); public static void main(string args[]) Prompt p = new Prompt(); String input = p.getline("type a line: "); System.out.println("User typed: " + input); static System.loadLibrary("Prompt"); Prompt.getLine 方 法 的 C 声 明 如 下 : JNIEXPORT jstring JNICALL Java_Prompt_getLine(JNIEnv *env, jobject this, jstring prompt); 3.1.2 Native Method Arguments Java_Prompt_getLine 接 收 3 个 参 数 : JNIEnv 结 构 包 括 JNI 函 数 表

第 二 个 参 数 的 意 义 取 决 于 该 方 法 是 静 态 还 是 实 例 方 法 (static or an instance method) 当 本 地 方 法 作 为 一 个 实 例 方 法 时, 第 二 个 参 数 相 当 于 对 象 本 身, 即 this. 当 本 地 方 法 作 为 一 个 静 态 方 法 时, 指 向 所 在 类. 在 本 例 中,Java_Prompt_getLine 是 一 个 本 地 实 例 方 法 实 现, 所 以 jobject 指 向 对 象 本 身 译 注 : Java 语 言 中 类 与 对 象 的 联 系 与 区 别, 概 念 很 清 晰, 但 在 JNI 和 VM 中, 有 一 些 问 题 需 要 说 明, 后 有 专 门 文 章 阐 述 3.1.3 Mapping of Types 在 native method 中 声 明 的 参 数 类 型, 在 JNI 中 都 有 对 应 的 类 型. 在 Java 中 有 两 类 数 据 类 型 :primitive types, 如,int, float, char; 另 一 种 为 reference types, 如, 类, 实 例, 数 组 译 者 : 数 组, 不 管 是 对 象 数 组 还 是 基 本 类 型 数 组, 都 作 为 reference types 存 在, 并 有 专 门 的 JNI 方 法 取 数 组 中 每 个 元 素. Java 与 JNI 基 本 类 型 的 映 射 很 直 接, 如 下 : Java boolean byte char short int long float Native(jni.h) jboolean jbyte jchar jshort jint jlong jfloat

double jdouble 相 比 基 本 类 型, 对 象 类 型 的 传 递 要 复 杂 很 多 Java 层 对 象 作 为 opaque references( 指 针 ) 传 递 到 JNI 层 Opaque references 是 一 种 C 的 指 针 类 型, 它 指 向 JavaVM 内 部 数 据 结 构 使 用 这 种 指 针 的 目 的 是 : 不 希 望 JNI 用 户 了 解 JavaVM 内 部 数 据 结 构 对 Opaque reference 所 指 结 构 的 操 作, 都 要 通 过 JNI 方 法 进 行. 比 如,"java.lang.String" 对 象,JNI 层 对 应 的 类 型 为 jstring, 对 该 opaque reference 的 操 作 要 通 过 JNIEnv->GetStringUTFChars 进 行 译 注 : 一 定 要 按 这 种 原 则 编 程, 千 万 不 要 为 了 效 率 或 容 易 的 取 到 某 个 值, 绕 过 JNI, 直 接 操 作 opaque reference. JNI 是 一 套 完 善 接 口, 所 有 需 求 都 能 满 足 在 JNI 中 对 象 的 基 类 即 为 jobject. 为 方 便 起 见, 还 定 义 了 jstring,jclass, jobjectarray 等 结 构, 他 们 都 继 承 自 jobject 3.2 Accessing Strings 如 下 使 用 方 式 是 错 误 的, 因 为 jstring 不 同 于 C 中 的 char * 类 型 JNIEXPORT jstring JNICALL Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt) /* ERROR: incorrect use of jstring as a char* pointer */ printf("%s", prompt);... 3.2.1 Converting to Native Strings 使 用 对 应 的 JNI 函 数 把 jstring 转 成 C/C++ 字 串 JNI 支 持 Unicode/UTF-8 字 符 编 码 互 转 Unicode 以 16-bits 值 编 码 ;UTF-8 是 一 种 以 字 节 为 单 位 变 长 格 式 的 字 符 编 码, 并 与 7-bits ASCII 码 兼 容 UTF-8 字 串 与 C 字 串 一 样, 以 NULL('\0') 做 结 束 符, 当 UTF-8 包 含 非 ASCII 码 字 符 时, 以 '\0' 做 结 束 符 的 规 则 不 变 7-bit ASCII 字 符 的 取 值 范 围 在 1-127 之 间, 这 些 字 符 的 值 域 与 UTF-8 中 相 同 当 最 高 位 被 设 置 时, 表 示 多 字 节 编 码 如 下, 调 用 GetStringUTFChars, 把 一 个 Unicode 字 串 转 成 UTF-8 格 式 字 串, 如 果 你 确 定 字 串 只 包 含 7-bit ASCII 字 符 这 个 字 串 可 以 使 用 C 库 中 的 相 关 函 数, 如 printf. 如 何 操 作 non-ascii 字 符, 后 面 有 介 绍 JNIEXPORT jstring JNICALL

Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt) char buf[128]; const jbyte *str; str = (*env)->getstringutfchars(env, prompt, NULL); if (str == NULL) return NULL; /* OutOfMemoryError already thrown */ printf("%s", str); (*env)->releasestringutfchars(env, prompt, str); /* We assume here that the user does not type more than * 127 characters */ scanf("%127s", buf); return (*env)->newstringutf(env, buf); 记 得 检 测 GetStringUTFChars 的 返 回 值, 因 为 调 用 该 函 数 会 有 内 存 分 配 操 作, 失 败 后, 该 函 数 返 回 NULL, 并 抛 OutOfMemoryError 异 常 如 何 处 理 异 常, 后 面 会 有 介 绍 JNI 处 理 异 常, 不 同 于 Java 中 的 try...catch 在 JNI 中, 发 生 异 常, 不 会 改 变 代 码 执 行 轨 迹, 所 以, 当 返 回 NULL, 要 及 时 返 回, 或 马 上 处 理 异 常 3.2.2 Freeing Native String Resources 调 用 ReleaseStringUTFChars 释 放 GetStringUTFChars 中 分 配 的 内 存 (Unicode -> UTF-8 转 换 的 原 因 ) 3.2.3 Constructing New Strings 使 用 JNIEnv->NewStringUTF 构 造 java.lang.string; 如 果 此 时 没 有 足 够 的 内 存, NewStringUTF 将 抛 OutOfMemoryError 异 常, 同 时 返 回 NULL 3.2.4 Other JNI String Functions 除 了 GetStringUTFChars, ReleaseStringUTFChars, 和 NewStringUTF, JNI 还 支 持 其 他 操 作 String 的 函 数 供 使 用 GetStringChars 是 有 Java 内 部 Unicode 到 本 地 UTF-8 的 转 换 函 数, 可 以 调 用 GetStringLength, 获 得 以 Unicode 编 码 的 字 串 长 度 也 可 以 使 用 strlen 计 算 GetStringUTFChars 的 返 回 值, 得 到 字 串 长 度 const jchar * GetStringChars(JNIEnv *env, jstring str, jboolean *iscopy);

上 述 声 明 中, 有 iscopy 参 数, 当 该 值 为 JNI_TRUE, 将 返 回 str 的 一 个 拷 贝 ; 为 JNI_FALSE 将 直 接 指 向 str 的 内 容 注 意 : 当 iscopy 为 JNI_FALSE, 不 要 修 改 返 回 值, 不 然 将 改 变 java.lang.string 的 不 可 变 语 义 一 般 会 把 iscopy 设 为 NULL, 不 关 心 Java VM 对 返 回 的 指 针 是 否 直 接 指 向 java.lang.string 的 内 容 一 般 不 能 预 知 VM 是 否 会 拷 贝 java.lang.string 的 内 容, 程 序 员 应 该 假 设 GetStringChars 会 为 java.lang.string 分 配 内 存 在 JavaVM 的 实 现 中, 垃 圾 回 收 机 制 会 移 动 对 象, 并 为 对 象 重 新 配 置 内 存 一 但 java.lang.string 占 用 的 内 存 暂 时 无 法 被 GC 重 新 配 置, 将 产 生 内 存 碎 片, 过 多 的 内 存 碎 片, 会 更 频 繁 的 出 现 内 存 不 足 的 假 象 记 住 在 调 用 GetStringChars 之 后, 要 调 用 ReleaseStringChars 做 释 放, 不 管 在 调 用 GetStringChars 时 为 iscopy 赋 值 JNI_TRUE 还 是 JNI_FALSE, 因 不 同 JavaVM 实 现 的 原 因, ReleaseStringChars 可 能 释 放 内 存, 也 可 能 释 放 一 个 内 存 占 用 标 记 (iscopy 参 数 的 作 用, 从 GetStringChars 返 回 一 个 指 针, 该 指 针 直 接 指 向 String 的 内 容, 为 了 避 免 该 指 针 指 向 的 内 容 被 GC, 要 对 该 内 存 做 锁 定 标 记 ) 3.2.5 New JNI String Function in Java 2 SDK Release 1.2 为 尽 可 能 的 避 免 内 存 分 配, 返 回 指 向 java.lang.string 内 容 的 指 针,Java 2 SDK release 1.2 提 供 了 :Get/RleaseStringCritical. 这 对 函 数 有 严 格 的 使 用 原 则 当 使 用 这 对 函 数 时, 这 对 函 数 间 的 代 码 应 被 当 做 临 界 区 (critical region). 在 该 代 码 区, 不 要 调 用 任 何 会 阻 塞 当 前 线 程 和 分 配 对 象 的 JNI 函 数, 如 IO 之 类 的 操 作 上 述 原 则, 可 以 避 免 JavaVM 执 行 GC 因 为 在 执 行 Get/ReleaseStringCritical 区 的 代 码 时,GC 被 禁 用 了, 如 果 因 某 些 原 因 在 其 他 线 程 中 引 发 了 JavaVM 执 行 GC 操 作,VM 有 死 锁 的 危 险 : 当 前 线 程 A 进 入 Get/RelaseStringCritical 区, 禁 用 了 GC, 如 果 其 他 线 程 B 中 有 GC 请 求, 因 A 线 程 禁 用 了 GC, 所 以 B 线 程 被 阻 塞 了 ; 而 此 时, 如 果 B 线 程 被 阻 塞 时 已 经 获 得 了 一 个 A 线 程 执 行 后 续 工 作 时 需 要 的 锁 ; 死 锁 发 生 了 可 以 嵌 套 调 用 GetStringCritical: jchar *s1, *s2; s1 = (*env)->getstringcritical(env, jstr1); if (s1 == NULL)... /* error handling */ s2 = (*env)->getstringcritical(env, jstr2); if (s2 == NULL)

(*env)->releasestringcritical(env, jstr1, s... /* error handling */... /* use s1 and s2 */ (*env)->releasestringcritical(env, jstr1, s1); (*env)->releasestringcritical(env, jstr2, s2); GetStringCritical 因 VM 实 现 的 原 因, 会 涉 及 内 存 操 作, 所 以 我 们 需 要 检 查 返 回 指. 比 如, 对 于 java.lang.string 来 说,VM 内 部 并 不 是 连 续 存 储 的, 所 以 GetStringCritical 要 返 回 一 个 连 续 的 字 符 数 组, 必 然 要 有 内 存 操 作 为 避 免 死 锁, 此 时 应 尽 量 避 免 调 用 其 他 JNI 方 法, 只 允 许 调 用 GetStringCritical/ReleaseStringCritical,Get/ReleasePrimitiveArrayCritical 因 VM 内 部 Unicode 编 码 的 缘 故, 所 以 Get/ReleaseStringUTFCritical 这 种 涉 及 Unicode->UTF8 转 换 要 分 配 内 存 的 函 数 不 支 持 GetStringRegion/GetStringUTFRegion, 向 准 备 好 的 缓 冲 区 赋 值, 如 下 : JNIEXPORT jstring JNICALL Java_Prompt_getLine(JNIEnv *env, jobject obj, jstring prompt) /*assumethepromptstringanduserinputhaslessthan128 characters */ char outbuf[128], inbuf[128]; int len = (*env)->getstringlength(env, prompt); (*env)->getstringutfregion(env, prompt, 0, len, outbuf); printf("%s", outbuf); scanf("%s", inbuf); return (*env)->newstringutf(env, inbuf); GetStringUTFRegion 有 两 个 参 数,starting index 和 length, 这 两 个 参 数 以 Unicode 编 码 计 算. 该 函 数 会 做 边 界 检 查, 所 以 可 能 抛 出 StringIndexOutOfBoundsException 因 为 该 函 数 不 涉 及 内 存 操 作, 所 以 较 GetStringUTFChars 使 用 要 简 单 译 注 : 有 两 个 函 数 :GetStringLength/GetStringUTFLength, 前 者 是 Unicode 编 码 长 度, 后 者 是 UTF 编 码 长 度 GetStringUTFRegion 很 有 用, 因 为 你 不 能 修 改 GetStringUTFChars 返 回 值, 所 以 需 要 另 外 malloc/strcpy 之 后, 再 操 作 返 回 值, 耗 时 费 力, 不 如 直 接 使 用 GetStringUTFRegion 来 的 简 洁 高 效 3.2.6 Summary of JNI String Functions

JNI Function Description Since GetStringChars ReleaseStringChars GetStringUTFChars ReleaseStringUTFChars Obtains or releases a pointer to the contents of a string in Unicode format.may return a copy of the string. Obtains or releases a pointer to the contents of a string in UTF-8 format. May return a copy of the string. JDK1.1 JDK1.1 GetStringLength GetStringUTFLength NewString NewStringUTF GetStringCritical ReleaseStringCritical GetStringRegion SetStringRegion GetStringUTFRegion SetStringUTFRegion Returns the number of Unicode characters in the string. Returns the number of bytes needed(not including the trailing 0) to represent a string in the UTF- 8 format. Creates a java.lang.string instance that contains the same sequence of characters as the given Unicode C string. Creates a java.lang.string instance that contains the same sequence of characters as the given UTF-8 encoded C string. Obtains a pointer to the contents of a string in Unicode format. May return a copy of the string. Native code must not block between a pair of Get/ ReleaseStringCritical calls. Copies the contents of a string to or from a preallocated C buffer in the Unicode format. Copies the content of a string to or from a preallocated C buffer in the UTF-8 format. JDK1.1 JDK1.1 JDK1.1 JDK1.1 Java 2 SDK1.2 Java 2 SDK1.2 Java 2 SDK1.2 3.2.7 Choosing among the String Functions 该 表 给 出 了 选 择 字 符 串 函 数 的 策 略 :

如 果 你 使 用 JDK 1.1 或 JDK 1.2, 你 只 能 使 用 Get/ReleaseStringChars 和 Get/ReleaseStringUTFChars 对 于 小 尺 寸 字 串 的 操 作, 首 选 Get/SetStringRegion 和 Get/SetStringUTFRegion, 因 为 栈 上 空 间 分 配, 开 销 要 小 的 多 ; 而 且 没 有 内 存 分 配, 就 不 会 有 out-of-memory exception 如 果 你 要 操 作 一 个 字 串 的 子 集, 本 套 函 数 的 starting index 和 length 正 合 要 求 GetStringCritical 必 须 非 常 小 心 使 用 你 必 须 确 保 不 分 配 新 对 象 和 任 何 阻 塞 系 统 的 操 作, 以 避 免 发 生 死 锁 如 下, 因 调 用 fprintf, 该 c 函 数 要 执 行 IO 操 作, 所 以 是 不 安 全 的 /* This is not safe! */ const char *c_str = (*env)->getstringcritical(env, j_str, 0); if (c_str == NULL)... /* error handling */ fprintf(fd, "%s\n", c_str); (*env)->releasestringcritical(env, j_str, c_str); 上 述 代 码, 不 安 全 的 原 因 : 当 前 线 程 执 行 了 GetStringCritical 后 将 禁 用 GC. 假 设,T 线 程 正 等 待 从 fd 读 取 数 据. 进 一 步 假 设, 调 用 fprintf 时 使 用 的 系 统 缓 存 将 等 待 T 读 取 完 毕 后 设 置. 我 们 制 造 了 一 个 死 锁 情 景 : 如 果 T 在 读 取 数 据 时 有 内 存 分 配 需 求, 可 能 使

JavaVM 执 行 GC. 而 此 时 的 GC 请 求 将 被 阻 塞, 直 到 当 前 线 程 执 行 ReleaseStringCritical, 不 幸 的 时, 这 个 操 作 必 须 等 fprintf 调 用 完 毕 后 才 会 执 行 此 时, 死 锁 发 生 所 以, 当 你 调 用 Get/RleaseStringCritical 要 时 刻 警 惕 死 锁 3.3 Accessing Arrays JNI 对 每 种 数 据 类 型 的 数 组 都 有 对 应 的 函 数 class IntArray private native int sumarray(int[] arr); public static void main(string[] args) IntArray p = new IntArray(); int arr[] = new int[10]; for (int i = 0; i < 10; i++) arr[i] = i; int sum = p.sumarray(arr); System.out.println("sum = " + sum); static System.loadLibrary("IntArray"); 3.3.1 Accessing Arrays in C 如 下 直 接 操 作 数 组 是 错 误 的 : /* This program is illegal! */ JNIEXPORT jint JNICALL Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintarray arr) int i, sum = 0; for (i = 0; i < 10; i++) sum += arr[i]; 如 下 操 作 正 确 : JNIEXPORT jint JNICALL

Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintarray arr) jint buf[10]; jint i, sum = 0; (*env)->getintarrayregion(env, arr, 0, 10, buf); for (i = 0; i < 10; i++) sum += buf[i]; return sum; JNI 中 数 组 的 基 类 为 jarray, 其 他 如 jintarray 都 继 承 自 jarray 3.3.2 Accessing Arrays of Primitive Types 上 节 示 例 中, 使 用 GetIntArrayRegion 拷 贝 数 组 内 容 到 buf 中, 这 里 没 有 做 越 界 异 常 检 测, 因 为 知 道 数 组 有 10 个, 参 数 3 为 待 拷 贝 数 组 的 起 始 位 置, 参 数 4 为 拷 贝 元 素 的 个 数 JNI 支 持 SetIntArrayRegion 允 许 重 新 设 置 数 组 一 个 区 域 的 值, 其 他 基 本 类 型 (boolean, short, 和 float) 也 有 对 应 的 支 持 JNI 支 持 通 过 Get/Release<Type>ArrayElemetns 返 回 Java 数 组 的 一 个 拷 贝 ( 实 现 优 良 的 VM, 会 返 回 指 向 Java 数 组 的 一 个 直 接 的 指 针, 并 标 记 该 内 存 区 域, 不 允 许 被 GC) JNIEXPORT jint JNICALL Java_IntArray_sumArray(JNIEnv *env, jobject obj, jintarray arr) jint *carr; jint i, sum = 0; carr = (*env)->getintarrayelements(env, arr, NULL); if (carr == NULL) return 0; /* exception occurred */ for (i=0; i<10; i++) sum += carr[i]; (*env)->releaseintarrayelements(env, arr, carr, 0); return sum; GetArrayLength 返 回 数 组 元 素 个 数 Java 2 SDK release 1.2 支 持 Get/ReleasePrimitiveArrayCritical, 该 套 函 数 的 使 用 原 则 与 上 述 String 部 分 相 同

3.3.3 Summary of JNI Primitive Array Functions JNI Function Description Since Get<Type>ArrayRegion Set<Type>ArrayRegion Get<Type>ArrayElements Release<Type>ArrayElements GetArrayLength New<Type>Array GetPrimitiveArrayCritical ReleasePrimitiveArrayCritica l Copies the contents of primitive arrays to or from a preallocated C buffer. Obtains a pointer to the contents of a primitive array.may return a copy of the array. Returns the number of elements in the array. Creates an array with the given length. Obtains or releases a pointer to the contents of a primitive array. May disable garbage collection, or return a copy of the array. JDK1.1 JDK1.1 JDK1.1 JDK1.1 Java 2 SDK1.2 3.3.4 Choosing among the Primitive Array Functions 使 用 原 则, 与 上 述 String 部 分 相 同, 请 阅 读 原 文 或 回 顾 前 面 的 内 容

3.3.5 Accessing Arrays of Objects 对 于 对 象 数 组 的 访 问, 使 用 Get/SetObjectArrayElement, 对 象 数 组 只 提 供 针 对 数 组 的 每 个 元 素 的 Get/Set, 不 提 供 类 似 Region 的 区 域 性 操 作 如 下, 二 维 数 组 示 例,Java 部 分 class ObjectArrayTest private static native int[][] initint2darray(int size); public static void main(string[] args) int[][] i2arr = initint2darray(3); for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) System.out.print(" " + i2arr[i][j]); System.out.println(); static System.loadLibrary("ObjectArrayTest"); JNI 部 分 : JNIEXPORT jobjectarray JNICALL Java_ObjectArrayTest_initInt2DArray(JNIEnv *env, jclass cls, int size) jobjectarray result; int i; jclass intarrcls = (*env)->findclass(env, "[I"); if (intarrcls == NULL) return NULL; /* exception thrown */ result = (*env)->newobjectarray(env, size, intarrcls, NULL); if (result == NULL) return NULL; /* out of memory error thrown */ for (i = 0; i < size; i++) jint tmp[256]; /* make sure it is large enough! */ int j;

jintarray iarr = (*env)->newintarray(env, size); if (iarr == NULL) return NULL; /* out of memory error thrown */ for (j = 0; j < size; j++) tmp[j] = i + j; (*env)->setintarrayregion(env, iarr, 0, size, tmp); (*env)->setobjectarrayelement(env, result, i, iarr); (*env)->deletelocalref(env, iarr); return result; newint2darray 方 法 首 先 调 用 FindClass 获 得 一 个 一 维 int 数 组. "[I" 作 为 JNI 类 描 述 符 等 价 于 Java int[] 声 明 FindClass 当 装 载 类 失 败, 返 回 NULL( 可 能 是 没 找 到 类 或 内 存 不 足 ) 译 注 : 类 描 述 符, 也 可 以 叫 做 " 类 签 名 " 签 名 的 作 用 : 为 了 准 确 描 述 一 件 事 物. Java Vm 定 义 了 类 签 名, 方 法 签 名 ; 其 中 方 法 签 名 是 为 了 支 持 方 法 重 载 FindClass 返 回 NULL 的 原 因 : 提 供 了 错 误 的 类 描 述 符 无 法 在 当 前 ClassLoader 上 下 文 中 找 到 类 解 决 办 法 : 认 真 检 查 类 描 述 符 是 否 正 确 以 "/" 作 为 包 分 隔 符, 即 类 描 述 符 的 形 式 为 "xxx/xxx/xxx", 而 非 "xxx.xxx.xxx", 也 可 简 单 记 忆 为 "/" 用 在 本 地 形 式 ( 或 虚 拟 机 ) 中 ;"." 分 隔 符, 用 在 Java(Java Programming Language) 环 境 中 ; 并 且 类 描 述 符 末 尾 没 有 ".java", 如 FindClass("java/lang/String") 而 非 FindClass("java/lang/String.java") 构 造 ClassLoader, 并 利 用 Class.forName(String name, boolean initialize, ClassLoader loader) 装 载 类 其 中 第 三 个 解 决 办 法 比 较 复 杂, 涉 及 到 Java 的 双 亲 委 派 模 型, 类 与 对 象 相 容 性 判 定 等 问 题, 将 有 专 门 文 章 阐 述 然 后 调 用 NewObjectArray 分 配 一 个 对 象 数 组 注 意," 基 本 类 型 数 组 " 这 是 个 整 体 的 概 念, 它 是 一 个 对 象 后 面 我 们 要 填 充 它 注 意,DeleteLocalRef 是 释 放 局 部 对 象 引 用 译 注 : Java 中 有 许 多 引 用 的 概 念, 我 们 只 关 心 GlobalRef 和 LocalRef 两 种 JNI 编 程 很 复 杂, 建 议 不 要 引 入 更 多 复 杂 的 东 西, 正 确 高 效 的 实 现 功 能 就 可 以 了 比 如 对 引 用 来 说, 最 好

不 要 在 JNI 中 考 虑 : 虚 引 用 和 影 子 引 用 等 复 杂 的 东 西 GlobalRef: 当 你 需 要 在 JNI 层 维 护 一 个 Java 对 象 的 引 用, 而 避 免 该 对 象 被 垃 圾 回 收 时, 使 用 NewGlobalRef 告 诉 VM 不 要 回 收 此 对 象, 当 本 地 代 码 最 终 结 束 该 对 象 的 引 用 时, 用 DeleteGlobalRef 释 放 之 LocalRef: 每 个 被 创 建 的 Java 对 象, 首 先 会 被 加 入 一 个 LocalRef Table, 这 个 Table 大 小 是 有 限 的, 当 超 出 限 制,VM 会 报 LocalRef Overflow Exception, 然 后 崩 溃. 这 个 问 题 是 JNI 编 程 中 经 常 碰 到 的 问 题, 请 引 起 高 度 警 惕, 在 JNI 中 及 时 通 过 DeleteLocalRef 释 放 对 象 的 LocalRef. 又,JNI 中 提 供 了 一 套 函 数 :Push/PopLocalFrame, 因 为 LocalRef Table 大 小 是 固 定 的, 这 套 函 数 只 是 执 行 类 似 函 数 调 用 时, 执 行 的 压 栈 操 作, 在 LocalRef Table 中 预 留 一 部 分 供 当 前 函 数 使 用, 当 你 在 JNI 中 产 生 大 量 对 象 时, 虚 拟 机 仍 然 会 因 LocalRef Overflow Exception 崩 溃, 所 以 使 用 该 套 函 数 你 要 对 LocalRef 使 用 量 有 准 确 估 计

CHAPTER 4 Fields and Methods 本 章 介 绍 如 何 访 问 对 象 成 员, 如 何 从 本 地 代 码 调 用 Java 方 法, 即 以 callback 方 式 从 本 地 代 码 调 用 Java 代 码 ; 最 后 介 绍 一 些 优 化 技 术 4.1 Accessing Fields Java 语 言 支 持 两 种 成 员 (field):(static) 静 态 成 员 和 实 例 成 员. 在 JNI 获 取 和 赋 值 成 员 的 方 法 是 不 同 的. 译 者 : Java 层 的 field 和 method, 不 管 它 是 public, 还 是 package private 和 protected, 从 JNI 都 可 以 访 问 到,Java 面 向 语 言 的 封 装 性 不 见 了 Java: class InstanceFieldAccess private String s; private native void accessfield(); public static void main(string args[]) InstanceFieldAccess c = new InstanceFieldAccess(); c.s = "abc"; c.accessfield(); System.out.println("In Java:"); System.out.println(" c.s = \"" + c.s + "\""); static System.loadLibrary("InstanceFieldAccess"); JNI: JNIEXPORT void JNICALL Java_InstanceFieldAccess_accessField(JNIEnv *env, jobject obj) jfieldid fid; /* store the field ID */ jstring jstr; const char *str; /* Get a reference to obj s class */

jclass cls = (*env)->getobjectclass(env, obj); printf("in C:\n"); /* Look for the instance field s in cls */ fid = (*env)->getfieldid(env, cls, "s", "Ljava/lang/String;"); if (fid == NULL) return; /* failed to find the field */ /* Read the instance field s */ jstr = (*env)->getobjectfield(env, obj, fid); str = (*env)->getstringutfchars(env, jstr, NULL); if (str == NULL) return; /* out of memory */ printf(" c.s = \"%s\"\n", str); (*env)->releasestringutfchars(env, jstr, str); /* Create a new string and overwrite the instance field */ jstr = (*env)->newstringutf(env, "123"); if (jstr == NULL) return; /* out of memory */ (*env)->setobjectfield(env, obj, fid, jstr); 输 出 : In C: c.s = "abc" In Java: c.s = "123" 4.1.1 Procedure for Accessing an Instance Field 访 问 对 象 成 员 分 两 步, 首 先 通 过 GetFieldID 得 到 对 象 成 员 ID, 如 下 : fid = (*env)->getfieldid(env, cls, "s", "Ljava/lang/String;"); 示 例 代 码, 通 过 GetObjectClass 从 obj 对 象 得 到 cls. 这 时, 通 过 在 对 象 上 调 用 下 述 方 法 获 得 成 员 的 值 : jstr = (*env)->getobjectfield(env, obj, fid); 示 例 中 要 得 到 的 是 一 个 对 象 类 型, 所 以 用 GetObjectField. 此 外 JNI 还 提 供 Get/SetIntField,Get/SetFloatField 访 问 不 同 类 型 成 员

译 者 : 通 过 JNI 方 法 访 问 对 象 的 成 员,JNI 对 应 的 函 数 命 名 非 常 有 规 律, 即 Get/Set<Return Value Type>Field 4.1.2 Field Descriptors 此 章 主 要 讲 述 签 名 问 题, 较 繁 琐, 可 以 总 结 如 下 : Type Signature Java Type Z boolean B byte C char S short I int J long F float D double L fully-qualified-class ; fully-qualified-class [ type type[] ( arg-types ) ret-type method type 如 下 Java 方 法 : long f (int n, String s, int[] arr); signature: "(ILjava/lang/String;[I)J" 签 名 是 一 种 用 参 数 个 数 和 类 型 区 分 同 名 方 法 的 手 段, 即 解 决 方 法 重 载 问 题 其 中 要 特 别 注 意 的 是 : 1. 类 描 述 符 开 头 的 'L' 与 结 尾 的 ';' 必 须 要 有 2. 数 组 描 述 符, 开 头 的 '[' 必 须 有. 3. 方 法 描 述 符 规 则 : "( 各 参 数 描 述 符 ) 返 回 值 描 述 符 ", 其 中 参 数 描 述 符 间 没 有 任 何 分 隔 符 号 描 述 符 很 重 要, 请 烂 熟 于 心. 写 JNI, 对 于 错 误 的 签 名 一 定 要 特 别 敏 感, 此 时 编 译 器 帮 不 上 忙, 执 行 make 前 仔 细 检 查 你 的 代 码 4.1.3 Accessing Static Fields 静 态 成 员 访 问 与 实 例 成 员 类 似 Java:

class StaticFielcdAccess private static int si; private native void accessfield(); public static void main(string args[]) StaticFieldAccess c = new StaticFieldAccess(); StaticFieldAccess.si = 100; c.accessfield(); System.out.println("In Java:"); System.out.println(" StaticFieldAccess.si = " + si); static System.loadLibrary("StaticFieldAccess"); JNI: JNIEXPORT void JNICALL Java_StaticFieldAccess_accessField(JNIEnv *env, jobject obj) jfieldid fid; /* store the field ID */ jint si; /* Get a reference to obj s class */ jclass cls = (*env)->getobjectclass(env, obj); printf("in C:\n"); /* Look for the static field si in cls */ fid = (*env)->getstaticfieldid(env, cls, "si", "I"); if (fid == NULL) return; /* field not found */ /* Access the static field si */ si = (*env)->getstaticintfield(env, cls, fid); printf(" StaticFieldAccess.si = %d\n", si); (*env)->setstaticintfield(env, cls, fid, 200); 输 出 : In C: StaticFieldAccess.si = 100 In Java: StaticFieldAccess.si = 200

请 阅 读 上 述 代 码, 不 再 叙 述 4.2 Calling Methods Java 中 有 三 类 方 法 : 实 例 方 法 静 态 方 法 和 构 造 方 法 class InstanceMethodCall private native void nativemethod(); private void callback() System.out.println("In Java"); public static void main(string args[]) InstanceMethodCall c = new InstanceMethodCall(); c.nativemethod(); static System.loadLibrary("InstanceMethodCall"); JNIEXPORT void JNICALL Java_InstanceMethodCall_nativeMethod(JNIEnv *env, jobject obj) jclass cls = (*env)->getobjectclass(env, obj); jmethodid mid = (*env)->getmethodid(env, cls, "callback", "()V"); if (mid == NULL) return; /* method not found */ printf("in C\n"); (*env)->callvoidmethod(env, obj, mid); 输 出 : In C In Java 4.2.1 Calling Instance Methods 如 上 节 示 例, 回 调 Java 方 法 分 两 步 : 首 先 通 过 GetMethodID 在 给 定 类 中 查 询 方 法. 查 询 基 于 方 法 名 称 和 签 名 本 地 方 法 调 用 CallVoidMethod, 该 方 法 表 明 被 调 Java 方 法 的 返 回 值 为 void

译 者 : 从 JNI 调 用 实 例 方 法 命 名 规 则 :Call<Return Value Type>Method 4.2.2 Formaing the Method Descriptor 一 个 方 法 描 述 ( 签 名 ) 由 各 参 数 类 型 签 名 和 返 回 值 签 名 构 成. 参 数 签 名 在 前, 并 用 小 括 号 括 起. 具 体 描 述 请 参 照 上 文 4.1.2 4.2.3 Calling Static Methods 同 实 例 方 法, 回 调 Java 静 态 方 法 分 两 步 : 首 先 通 过 GetStaticMethodID 在 给 定 类 中 查 找 方 法 通 过 CallStatic<ReturnValueType>Method 调 用 静 态 方 法 与 实 例 方 法 的 不 同, 前 者 传 入 参 数 为 jclass, 后 者 为 jobject 4.2.4 Calling Instance Methods of a Superclass 调 用 被 子 类 覆 盖 的 父 类 方 法 : JNI 支 持 用 CallNonvirtual<Type>Method 满 足 这 类 需 求 : GetMethodID 获 得 method ID 调 用 CallNonvirtualVoidMethod, CallNonvirtualBooleanMethod 上 述, 等 价 于 如 下 Java 语 言 的 方 式 : super.f(); CallNonvirtualVoidMethod 可 以 调 用 构 造 函 数 4.3 Invoking Constructors 你 可 以 像 调 用 实 例 方 法 一 样, 调 用 构 造 方 法, 只 是 此 时 构 造 函 数 的 名 称 叫 做 "<init>". 如 下 构 造 java.lang.string 对 象 (JNI 为 了 方 便 有 个 对 应 的 NewString 做 下 面 所 有 工 作, 这 里 只 是 做 示 例 展 示 ): jstring MyNewString(JNIEnv *env, jchar *chars, jint len) jclass stringclass; jmethodid cid; jchararray elemarr; jstring result; stringclass = (*env)->findclass(env, "java/lang/string"); if (stringclass == NULL)

return NULL; /* exception thrown */ /* Get the method ID for the String(char[]) constructor */ cid = (*env)->getmethodid(env, stringclass, "<init>", "([C)V"); if (cid == NULL) return NULL; /* exception thrown */ /* Create a char[] that holds the string characters */ elemarr = (*env)->newchararray(env, len); if (elemarr == NULL) return NULL; /* exception thrown */ (*env)->setchararrayregion(env, elemarr, 0, len, chars); /* Construct a java.lang.string object */ result = (*env)->newobject(env, stringclass, cid, elemarr); /* Free local references */ (*env)->deletelocalref(env, elemarr); (*env)->deletelocalref(env, stringclass); return result; 首 先,FindClass 找 到 java.lang.string 的 jclass. 接 下 来, 用 GetMethodID 找 到 构 造 函 数 String(char[] chars) 的 MethodID. 此 时 用 NewCharArray 分 配 一 个 Char 数 组 对 象 NewObject 调 用 构 造 函 数 用 DeleteLocalRef 释 放 资 源 注 意 NewString 是 个 常 用 函 数, 所 以 在 JNI 中 直 接 被 支 持 了, 并 且 该 函 数 的 实 现 要 比 我 们 实 现 的 高 效 也 可 使 用 CallNonvirtualVoidMehtod 调 用 构 造 函 数. 如 下 代 码 : result = (*env)->newobject(env, stringclass, cid, elemarr); 可 被 替 换 为 : result = (*env)->allocobject(env, stringclass); if (result) (*env)->callnonvirtualvoidmethod(env, result, stringclass, cid, elemarr); /* we need to check for possible exceptions */ if ((*env)->exceptioncheck(env)) (*env)->deletelocalref(env, result); result = NULL;

AllocObject 创 建 一 个 未 初 始 化 的 对 象, 该 函 数 必 须 在 每 个 对 象 上 被 调 用 一 次 而 且 只 能 是 一 次 有 时 你 会 发 现 先 创 建 未 初 始 化 对 象 再 调 用 构 造 函 数 的 方 法 是 有 用 的 4.4 Caching Field and Method IDs 获 得 field 与 method IDs, 需 要 做 基 于 名 称 和 签 名 的 符 号 表 查 询, 此 过 程 可 以 被 优 化 基 本 想 法 是 : 只 在 第 一 次 使 用 ID 时 查 询, 然 后 缓 存 该 值. 有 两 个 缓 存 时 机 : 首 次 使 用 和 初 始 化 类 时 4.4.1 Caching at the Point of Use 如 下, 首 次 使 用 时, 缓 存 的 局 部 静 态 变 量 中, 避 免 每 次 调 用 计 算 JNIEXPORT void JNICALL Java_InstanceFieldAccess_accessField(JNIEnv *env, jobject obj) static jfieldid fid_s = NULL; /* cached field ID for s */ jclass cls = (*env)->getobjectclass(env, obj); jstring jstr; const char *str; if (fid_s == NULL) fid_s = (*env)->getfieldid(env, cls, "s", "Ljava/lang/String;"); if (fid_s == NULL) return; /* exception already thrown */ printf("in C:\n"); jstr = (*env)->getobjectfield(env, obj, fid_s); str = (*env)->getstringutfchars(env, jstr, NULL); if (str == NULL) return; /* out of memory */ printf(" c.s = \"%s\"\n", str); (*env)->releasestringutfchars(env, jstr, str); jstr = (*env)->newstringutf(env, "123"); if (jstr == NULL) return; /* out of memory */

(*env)->setobjectfield(env, obj, fid_s, jstr); 如 上, 静 态 变 量 fid_s 保 存 了 InstanceFieldAccess.s 的 filed ID 初 始 化 阶 段 静 态 变 量 被 赋 值 为 NULL 第 一 调 用 InstanceFieldAccess.accessField 时, 缓 存 fieldid 以 待 后 用 你 可 能 会 发 现 上 述 代 码 有 个 竞 争 条 件, 当 多 个 线 程 同 时 访 问 此 函 数 时, 可 能 会 同 时 计 算 一 个 field ID. 没 关 系, 此 处 的 竞 争 是 无 害 的, 因 为 即 使 在 多 个 线 程 中 同 时 计 算 该 field ID, 各 线 程 中 的 计 算 结 果 都 是 一 样 的 构 造 函 数 的 MethodID 也 可 被 缓 存, 如 下 : jstring MyNewString(JNIEnv *env, jchar *chars, jint len) jclass stringclass; jchararray elemarr; static jmethodid cid = NULL; jstring result; stringclass = (*env)->findclass(env, "java/lang/string"); if (stringclass == NULL) return NULL; /* exception thrown */ /* Note that cid is a static variable */ if (cid == NULL) /* Get the method ID for the String constructor */ cid = (*env)->getmethodid(env, stringclass, "<init>", "([C)V"); if (cid == NULL) return NULL; /* exception thrown */ /* Create a char[] that holds the string characters */ elemarr = (*env)->newchararray(env, len); if (elemarr == NULL) return NULL; /* exception thrown */ (*env)->setchararrayregion(env, elemarr, 0, len, chars); /* Construct a java.lang.string object */ result = (*env)->newobject(env, stringclass, cid, elemarr); /* Free local references */ (*env)->deletelocalref(env, elemarr); (*env)->deletelocalref(env, stringclass); return result;

4.4.2 Caching in the Defining Class's Initializer 上 述 第 一 次 使 用 缓 存 的 方 式, 每 次 都 有 与 NULL 的 判 断, 并 且 可 能 有 一 个 无 害 的 竞 争 条 件 而 初 始 化 类 时, 同 时 初 始 化 JNI 层 对 该 类 成 员 的 缓 存, 可 以 弥 补 上 述 缺 憾, 如 下 initids: Java 代 码 : class InstanceMethodCall private static native void initids(); private native void nativemethod(); private void callback() System.out.println("In Java"); public static void main(string args[]) InstanceMethodCall c = new InstanceMethodCall(); c.nativemethod(); static System.loadLibrary("InstanceMethodCall"); initids(); JNI 代 码 : jmethodid MID_InstanceMethodCall_callback; JNIEXPORT void JNICALL Java_InstanceMethodCall_initIDs(JNIEnv *env, jclass cls) MID_InstanceMethodCall_callback = (*env)->getmethodid(env, cls, "callback", "()V"); 译 注 : 还 可 以 改 进 上 述 缓 存 策 略 的 初 始 化 时 机, 第 一 种 方 法 的 缺 陷 文 中 已 经 提 了, 而 第 二 种 需 要 在 Java 代 码 主 动 调 用 JNI 作 缓 存 改 进 : 可 以 在 你 的 项 目 中 加 一 套 Hash 表, 封 装 FindClass,GetMethodID,GetFieldID 等 函 数, 查 询 的 所 有 操 作, 都 对 Hash 表 操 作, 如 首 次 FindClass 一 个 类, 这 时 可 以 把 一 个 类 的 所 有 成 员 缓 存 到 Hash 表 中, 用 名 字 + 签 名 做 键 值 译 者 所 做 项 目 引 入 了 这 个 优 化, 项 目 的 执 行 效 率 有 100 倍 的 提 高 ; 当 时 还 做 过 两 个 权 衡 : 1. 用 一 个 Hash 表, 还 是 每 个 类 一 个 Hash 表 2. 首 次 FindClass 类 时, 一 次 缓 存 所 有 的 成 员, 还 是 用 时 缓 存 最 终 做 的 选 择 是 : 为 了 降 低 冲 突, 每 个 类 一 个 Hash 表, 并 且 一 次 缓 存 一 个 类 的 所 有 成 员 当 然, 没 有 尽 善 尽 美 的 优 化 策 略, 我 们 做 到 这 个 层 次, 已 经 达 到 预 期 目 标, 没 有 继 续 深 入

4.4.3 Comparison between the Two Approaches to Caching IDs 在 对 Java 源 码 无 改 动 权 时 使 用 时 缓 存 是 一 种 合 理 的 解 决 方 案. 但 有 许 多 弊 端 : 无 害 的 竞 争 条 件 和 重 复 与 NULL 比 较 在 类 没 被 卸 载 时,MethodID 和 FieldID 一 直 有 效. 所 以 你 必 须 保 证 : 当 你 的 JNI 代 码 依 赖 这 些 缓 存 值 的 声 明 周 期 内, 该 类 不 会 被 卸 载 而 与 另 一 种 优 化 策 略, 连 同 类 的 初 始 化 缓 存 Method/Field ID, 每 当 类 再 次 被 装 载, 缓 存 值 会 被 更 新 所 以, 有 条 件 的 话, 更 安 全 的 优 化 策 略 是 : 连 同 类 的 初 始 化 缓 存 Method/Field ID 4.5 Performance of JNI Field and Method Operations 在 学 习 了 如 何 缓 存 field 和 method ID 的 优 化 技 术 后, 你 可 能 会 想 : 影 响 JNI 回 调 性 能 的 关 键 性 因 素 是 什 么? 在 效 率 方 面,JNI/Java 与 Java/JNI 和 Java/Java 间 对 比, 是 怎 样 的? 这 要 看 具 体 VM 实 现 的 JNI 效 率. 很 难 给 出 一 个 普 适 的 性 能 关 键 指 标. 取 而 代 之, 我 们 将 分 析 在 访 问 类 成 员 时 的 固 有 性 能 损 失 首 先 比 较 Java/native 和 Java/Java, 前 者 因 下 述 原 因 可 能 会 比 后 者 慢 : Java/native 与 Java/Java 的 调 用 约 定 不 同. 所 以,VM 必 须 在 调 用 前, 对 参 数 和 调 用 栈 做 特 殊 准 备 常 用 的 优 化 技 术 是 内 联. 相 比 Java/Java 调 用,Java/native 创 建 内 联 方 法 很 难 粗 略 估 计 : 执 行 一 个 Java/native 调 用 要 比 Java/Java 调 用 慢 2-3 倍. 也 可 能 有 一 些 VM 实 现,Java/native 调 用 性 能 与 Java/Java 相 当 ( 此 种 虚 拟 机,Java/native 使 用 Java/Java 相 同 的 调 用 约 定 ) native/java 调 用 效 率 可 能 与 Java/Java 有 10 倍 的 差 距, 因 为 VM 一 般 不 会 做 Callback 的 优 化 对 于 field 的 访 问, 将 没 什 么 不 同, 只 是 通 过 JNI 访 问 某 对 象 结 构 中 某 个 位 置 的 值 译 注 : 上 述 只 是 学 术 考 虑. 用 好 缓 存 的 优 化 策 略, 完 全 可 以 让 项 目 工 作 的 绝 对 出 色

CHAPTER 5 Local and Global References JNI 把 instance 和 array 类 型 的 指 针 对 外 公 布 为 opaque reference. 本 地 代 码 不 直 接 操 作 指 针, 而 是 通 过 JNI 函 数, 所 以 本 地 代 码 不 用 关 心 内 存 布 局. 关 于 reference, 这 里 还 有 更 丰 富 的 东 西 有 待 介 绍 : JNI 支 持 三 种 类 型 的 opaque reference:local references, global references, 和 weak global references Local 和 Global 引 用 有 不 同 的 生 命 周 期. Local Ref 在 native method 执 行 完 毕 后 被 JavaVM 自 动 释 放, 而 GlobalRef,WeakRef 在 程 序 员 主 动 释 放 前 一 直 有 效 各 种 引 用 都 有 使 用 范 围. 如 LocalRef 只 能 在 当 前 线 程 的 native method 中 使 用 本 章 将 详 细 讲 述 不 同 类 型 Ref 的 使 用 方 法, 正 确 管 理 JNI 引 用 是 程 序 健 壮 空 间 占 用 少 的 关 键 5.1 Local and Global References LocalRef 与 GlobalRef 的 差 异, 将 用 几 个 示 例 说 明 : 大 部 分 JNI 函 数 都 会 创 建 LocalRef, 如 NewObject 创 建 一 个 实 例, 并 返 回 一 个 指 向 该 实 例 的 LocalRef LocalRef 只 在 本 线 程 的 native method 中 有 效. 一 但 native method 返 回,LocalRef 将 被 释 放 不 要 缓 存 一 个 LocalRef, 并 企 图 在 下 次 进 入 该 JNI 方 法 时 使 用, 如 下 : /* This code is illegal */ jstring MyNewString(JNIEnv *env, jchar *chars, jint len) static jclass stringclass = NULL; jmethodid cid; jchararray elemarr; jstring result; if (stringclass == NULL) stringclass = (*env)->findclass(env, "java/lang/string"); if (stringclass == NULL) return NULL; /* exception thrown */

/* It is wrong to use the cached stringclass here, because it may be invalid. */ cid = (*env)->getmethodid(env, stringclass, "<init>", "([C)V");... elemarr = (*env)->newchararray(env, len);... result = (*env)->newobject(env, stringclass, cid, elemarr); (*env)->deletelocalref(env, elemarr); return result; 上 述 代 码, 企 图 重 复 使 用 FindClass(env, "java/lang/string") 的 返 回 值, 这 种 方 式 不 对, 因 为 FindClass 返 回 的 是 一 个 LocalRef. 请 设 想 以 下 代 码 : JNIEXPORT jstring JNICALL Java_C_f(JNIEnv *env, jobject this) char *c_str =...;... return MyNewString(c_str); 如 下, 两 次 调 用 f 这 个 本 地 方 法....... = C.f(); // The first call is perhaps OK.... = C.f(); // This would use an invalid local reference.... 第 一 次 调 用 可 能 正 确, 而 第 二 次 将 引 用 一 个 无 效 位 置, 因 为 第 二 次 企 图 使 用 存 在 静 态 变 量 中 的 LocalRef 有 两 种 方 式 让 LocalRef 无 效, 一,native method 返 回,JavaVM 自 动 释 放 LocalRef; 二, 用 DeleteLocalRef 主 动 释 放 既 然 LocalRef 会 被 JavaVM 自 动 释 放, 为 什 么 还 要 有 DeleteLocalRef? 因 为 LocalRef 是 阻 止 引 用 被 GC, 但 当 你 在 本 地 代 码 中 操 作 大 量 对 象 时, 而 LocalRefTable 又 是 有 限 的, 及 时 调 用 DeleteLocalRef, 会 释 放 LocalRef 在 LocalRefTable 中 所 占 位 置 并 使 对 象 及 时 得 到 回 收 LocalRef 只 在 创 建 该 对 象 的 线 程 中 有 效, 企 图 把 LocalRef 存 到 全 局 变 量 中 供 其 他 线 程 使 用 的 做 法 是 错 误 的 译 注 : 注 意 这 里 的 提 到 的 native method 返 回, 返 回 是 指 回 到 Java 层, 如 果 从 一 个 本 地 函 数 返

回 到 另 一 个 本 地 函 数,LocalRef 是 有 效 的 5.1.2 Global References 释 放 GlobalRef 前, 你 可 以 在 多 个 本 地 方 法 调 用 过 程 和 多 线 程 中 使 用 GlobalRef 所 引 对 象 与 LocalRef 类 似,GlobalRef 的 作 用 : 防 止 对 象 被 GC(garbage collected, 垃 圾 回 收 ) GlobalRef 与 LocalRef 不 同 的 是,LocalRef 一 般 自 动 创 建 ( 返 回 值 为 jobject/jclass 等 JNI 函 数 ), 而 GlobalRef 必 须 通 过 NewGlobalRef 由 程 序 员 主 动 创 建 如 下 : /* This code is OK */ jstring MyNewString(JNIEnv *env, jchar *chars, jint len) static jclass stringclass = NULL;... if (stringclass == NULL) jclass localrefcls = (*env)->findclass(env, "java/lang/string"); if (localrefcls == NULL) return NULL; /* exception thrown */ /* Create a global reference */ stringclass = (*env)->newglobalref(env, localrefcls); /* The local reference is no longer useful */ (*env)->deletelocalref(env, localrefcls); /* Is the global reference created successfully? */ if (stringclass == NULL) return NULL; /* out of memory exception thrown */... 该 例 做 了 修 改, 当 stringclass 为 NULL 时, 我 们 创 建 了 java.lang.string 的 GlboalRef, 并 删 除 了 对 应 的 LocalRef, 以 待 下 次 再 进 入 此 方 法 时, 使 用 stringclass 5.1.3 Weak Global References Weak Global Ref 用 NewGlobalWeakRef 于 DeleteGlobalWeakRef 进 行 创 建 和 删 除, 多 个 本 地 方 法 调 用 过 程 中 和 多 线 程 上 下 文 中 使 用 的 特 性 与 GlobalRef 相 同, 但 该 类 型 的 引 用 不 保 证 不 被 GC

前 述 示 例 MyNewString 中, 对 java.lang.string 声 明 GlobalRef 或 GlobalWeakRef 效 果 相 同, 因 为 java.lang.string 是 一 个 系 统 类 不 会 被 GC Weak Global Ref 使 用 在 允 许 被 GC 的 场 合, 如 内 存 紧 张 时 JNIEXPORT void JNICALL Java_mypkg_MyCls_f(JNIEnv *env, jobject self) static jclass mycls2 = NULL; if (mycls2 == NULL) jclass mycls2local = (*env)->findclass(env, "mypkg/mycls2"); if (mycls2local == NULL) return; /* can t find class */ mycls2 = NewWeakGlobalRef(env, mycls2local); if (mycls2 == NULL) return; /* out of memory */... /* use mycls2 */ 我 们 假 设,MyCls 与 MyCls2 有 同 样 的 生 命 周 期 ( 并 被 同 样 的 Class Loader 装 载 ), 类 似 MyCls 被 卸 载 而 MyCls2 没 被 卸 载 的 情 况 不 考 虑 如 果 发 生 这 种 情 况, 我 们 还 需 要 检 测 mycls2 是 否 还 执 行 的 对 象 仍 然 有 效 5.1.4 Comparing Reference 有 两 个 对 象, 用 如 下 方 法 比 较 相 容 性 : (*env)->issameobject(env, obj1, obj2) 如 果 相 容, 返 回 JNI_TRUE, 否 则 返 回 JNI_FALSE 与 NULL 的 比 较,LocalRef 与 GlobalRef 语 义 显 然, 前 提 是 释 放 了 两 个 引 用, 程 序 员 重 新 为 相 应 变 量 做 了 NULL 初 始 化 但 对 于 Weak Global Ref 来 说, 需 要 使 用 下 述 代 码 判 定 : (*env)->issameobject(env, wobj, NULL) 因 为, 对 于 一 个 Weak Global Ref 来 说 可 能 指 向 已 经 被 GC 的 无 效 对 象 译 注 : 上 述 的 判 断, 都 是 假 设 所 有 的 类 和 对 象 都 是 在 一 个 Class Loader 下 被 装 载 的. 关 于 ClassLoader 的 议 题 后 有 专 门 文 章.

5.2 Freeing Reference 每 个 JNI 引 用 都 会 引 用 表 中 的 一 个 位 置. 作 为 一 个 JNI 程 序 员, 你 应 该 清 楚 程 序 某 阶 段 中 使 用 的 引 用 数 量 如 LocalRef, 如 果 你 疏 于 DeleteLocalRef 的 话, 在 JavaVM 运 行 限 制 内 你 的 应 用 程 序 工 作 正 常, 在 极 端 情 况 会 崩 溃 5.2.1 Freeing Local References 译 注 : 本 章 没 有 翻 译. 本 章 讲 了 很 多 关 于 LocalRef 的 释 放 原 则, 译 者 认 为 : 考 虑 何 时 释 放 / 何 时 不 释 放 的 问 题, 不 如 认 真 审 查 代 码, 严 堵 每 个 泄 露 环 节, 尽 最 大 努 力 提 高 程 序 的 稳 定 性 就 像 内 存 分 配 一 样, 虽 然 进 程 结 束 后,OS 自 动 释 放 该 进 程 分 配 的 所 有 内 存, 但 对 于 期 望 长 期 稳 定 运 行 的 系 统 来 说, 我 们 希 望 杜 绝 内 存 泄 露 5.2.2 Managing Local References in Java 2 SDK Release 1.2 译 注 : 本 章 没 有 翻 译 由 于 LocalRef Table 大 小 是 固 定 的, 这 套 函 数 只 是 执 行 类 似 函 数 调 用 时, 执 行 的 压 栈 操 作, 并 在 执 行 PopLocalFrame 后 执 行 类 似 退 栈 操 作, 在 LocalRef Table 中 预 留 一 部 分 供 当 前 函 数 使 用, 当 你 在 JNI 中 产 生 大 量 对 象 时, 虚 拟 机 仍 然 会 因 LocalRef Overflow Exception 崩 溃 具 体 原 则 仍 如 上 述, 严 堵 每 个 泄 露 环 节 ; 如 果 你 能 准 确 估 计 LocalRef 用 量, 可 以 使 用 Push/PopLocalFrame 5.2.3 Freeing Global References 当 不 再 使 用 GlobalRef 所 指 对 象, 及 时 调 用 DeleteGlobalRef 释 放 对 象. 否 则,GC 将 不 回 收 该 对 象 对 于 DeleteWeakGlobalRef 来 说, 不 使 用 WeakGlobalRef 时, 也 要 及 时 释 放, 因 为 即 使 GC 会 回 收 该 对 象 内 容,WeakGlobalRef 在 Table 中 的 位 置 还 占 用 着, 即 和 尚 都 跑 了, 庙 还 在 译 注 : 综 上, 不 管 何 种 类 型 引 用, 在 不 使 用 所 引 用 对 象 后, 及 时 调 用 对 应 指 针 类 型 的 释 放 函 数 5.3 Rules for Managing References 现 在 我 们 归 纳 一 下 管 理 JNI 引 用 的 原 则. 看 看 如 何 减 少 内 存 使 用 有 效 使 用 对 象 有 两 类 本 地 函 数 : 功 能 函 数 和 工 具 函 数 当 写 native method 的 实 现 时, 要 认 真 处 理 循 环 中 产 生 的 LocalRef. VM 规 范 中 规 定 每 个 本 地 方 法 至 少 要 支 持 16 个 LocalRef 供 自 由 使 用 并 在 本 地 方 法 返 回 后 回 收. 本 地 方 法 绝 对 不 能 滥 用 GlobalRef 和 WeakGlobalRef, 因 为 此 类 型 引 用 不 会 被 自 动 回 收

工 具 函 数, 对 LocalRef 的 使 用 更 要 提 起 警 惕, 因 为 该 类 函 数 调 用 上 下 文 不 确 定, 而 且 会 被 重 复 调 用, 每 个 代 码 路 径 都 要 保 证 不 存 在 LocalRef 泄 露 由 于 某 些 缓 存 机 制, 可 以 在 工 具 函 数 中 创 建 GlobalRef, WeakGlobalRef 当 工 具 函 数 返 回 对 象 时, 要 严 格 遵 守 引 用 约 定, 让 调 用 者 在 决 定 是 否 释 放 时 能 作 出 准 确 判 断, 如 下 : while (JNI_TRUE) jstring infostring = GetInfoString(info);... /* process infostring */??? /* * we need to call DeleteLocalRef, DeleteGlobalRef, * or DeleteWeakGlobalRef depending on the type of * reference returned by GetInfoString. */ JNI 方 法 NewLocalRef 总 保 证 返 回 一 个 LocalRef, 如 下 : jstring MyNewString(JNIEnv *env, jchar *chars, jint len) static jstring result; /* wstrncmp compares two Unicode strings */ if (wstrncmp("commonstring", chars, len) == 0) /* refers to the global ref caching "CommonString" */ static jstring cachedstring = NULL; if (cachedstring == NULL) /* create cachedstring for the first time */ jstring cachedstringlocal =... ; /* cache the result in a global reference */ cachedstring = (*env)->newglobalref(env, cachedstringlocal); return (*env)->newlocalref(env, cachedstring);... /* create the string as a local reference and store in result as a local reference */

return result; Push/PopLocalFrame 常 被 用 来 管 理 LocalRef. 在 进 入 本 地 方 法 时, 调 用 一 次 PushLocalFrame, 并 在 本 地 方 法 结 束 时 调 用 PopLocalFrame. 此 对 方 法 执 行 效 率 非 常 高, 建 议 使 用 这 对 方 法 译 注 : 你 只 要 对 当 前 上 下 文 内 使 用 的 对 象 数 量 有 准 确 估 计, 建 议 使 用 这 对 方 法, 在 这 对 方 法 间, 不 必 调 用 DeleteLocalRef, 只 要 该 上 下 文 结 尾 处 调 用 PopLocalFrame 会 一 次 性 释 放 所 有 LocalRef 一 定 保 证 该 上 下 文 出 口 只 有 一 个, 或 每 个 return 语 句 都 做 严 格 检 查 是 否 调 用 了 PopLocalFrame jobject f(jnienv *env,...) jobject result; if ((*env)->pushlocalframe(env, 10) < 0) /* frame not pushed, no PopLocalFrame needed */ return NULL;... result =...; if (...) /* remember to pop local frame before return */ result = (*env)->poplocalframe(env, result); return result;... result = (*env)->poplocalframe(env, result); /* normal return */ return result; 忘 记 调 用 PopLocalFrame 可 能 会 使 VM 崩 溃

CHAPTER 6 Exceptions 我 们 已 经 碰 到 在 调 用 JNI 方 法 时 出 现 异 常 的 情 况. 本 章 将 介 绍 如 何 检 查 并 处 理 异 常 本 章 只 关 注 在 调 用 JNI 方 法 或 Java 方 法 时 出 现 异 常 的 处 理 办 法 (Java 异 常 ), 不 涉 及 本 地 代 码 本 身 ( 如 本 地 代 码 中 的 除 0 错 ) 或 调 用 系 统 函 数 出 现 异 常 的 处 理 方 法 6.1.1 Caching and Throwing Exceptions in Native Code 如 下 Java 代 码 展 示 如 何 声 明 JNI 可 能 抛 出 的 异 常 class CatchThrow private native void doit() throws IllegalArgumentException; private void callback() throws NullPointerException throw new NullPointerException("CatchThrow.callback"); public static void main(string args[]) CatchThrow c = new CatchThrow(); try c.doit(); catch (Exception e) System.out.println("In Java:\n\t" + e); static System.loadLibrary("CatchThrow"); JNI 代 码 : JNIEXPORT void JNICALL Java_CatchThrow_doit(JNIEnv *env, jobject obj) jthrowable exc; jclass cls = (*env)->getobjectclass(env, obj); jmethodid mid = (*env)->getmethodid(env, cls, "callback", "()V"); if (mid == NULL) return;

(*env)->callvoidmethod(env, obj, mid); exc = (*env)->exceptionoccurred(env); if (exc) /* We don't do much with the exception, except that we print a debug message for it, clear it, and throw a new exception. */ jclass newexccls; (*env)->exceptiondescribe(env); (*env)->exceptionclear(env); newexccls = (*env)->findclass(env, "java/lang/illegalargumentexception"); if (newexccls == NULL) /* Unable to find the exception class, give up. */ return; (*env)->thrownew(env, newexccls, "thrown from C code"); 输 出 : java.lang.nullpointerexception: at CatchThrow.callback(CatchThrow.java) at CatchThrow.doit(Native Method) at CatchThrow.main(CatchThrow.java) In Java: java.lang.illegalargumentexception: thrown from C code callback 方 法 抛 出 NullPointerException. 当 CallVoidMethod 把 控 制 权 返 回 给 本 地 代 码, 本 地 代 码 调 用 ExceptionOccurred 检 查 是 否 有 异 常 发 生. 我 们 的 处 理 方 式 是, 当 有 异 常 发 生, 调 用 ExceptionDescribe 打 印 调 用 堆 栈, 然 后 用 ExceptionClear 清 空 异 常, 最 后 重 新 抛 出 IllegalArgumentException 译 者 : ExceptionOccurred 返 回 一 个 jobject, 注 意 结 束 处 理 时 调 用 DeleteLocalRef 删 除 该 返 回 值 JNI 中 还 有 一 个 ExceptionCheck, 只 是 返 回 一 个 jboolean 的 布 尔 值, 更 适 合 检 查 异 常 是 否 发 生 在 JNI 中 产 生 的 异 常 ( 通 过 调 用 ThrowNew), 与 Java 语 言 中 异 常 发 生 的 行 为 不 同,JNI 中 当 前 代 码 路 径 不 会 立 即 改 变 在 Java 中 发 生 异 常,VM 自 动 把 控 制 权 转 向 try/catch 中 匹 配

的 异 常 类 型 处 理 块 VM 首 先 清 空 异 常 队 列, 然 后 执 行 异 常 处 理 块 相 反,JNI 中 必 须 显 式 处 理 VM 的 处 理 方 式 6.1.2 A Utility Function JNI 中 抛 异 常 很 经 典 : 找 异 常 类, 调 用 ThrowNew 抛 出 之 ; 所 以, 可 以 写 一 个 工 具 函 数 void JNU_ThrowByName(JNIEnv *env, const char *name, const char *msg) jclass cls = (*env)->findclass(env, name); /* if cls is NULL, an exception has already been thrown */ if (cls!= NULL) (*env)->thrownew(env, cls, msg); /* free the local ref */ (*env)->deletelocalref(env, cls); 本 书 中,JNU 前 缀 表 示 JNI Utilities. JNU_ThrowByName 首 先 通 过 FindClass 找 到 异 常 类 如 果 FindClass 找 类 失 败, 将 返 回 NULL, 并 抛 出 NoClassDefFoundError 异 常 此 情 况, JNU_ThrowByName 将 保 留 该 异 常, 然 后 返 回. 如 果 FindClass 成 功, 将 调 用 ThrowNew 抛 出 异 常 所 以 不 管 哪 种 情 况, 调 用 该 函 数 后, 当 前 的 JNIEnv 环 境 里 总 有 个 异 常 6.2 Proper Exception Handling JNI 程 序 员 应 对 所 有 可 能 的 异 常 做 处 理, 这 个 要 求 虽 然 苛 刻, 但 这 是 健 壮 软 件 的 保 证 6.2.1 Checking for Exception 有 两 种 方 式 检 查 是 否 有 异 常 发 生 1. 大 多 数 JNI 函 数 用 显 式 方 式 表 明 当 前 线 程 是 否 有 异 常 发 生 下 述 代 码 判 断 GetFieldID 返 回 是 否 为 NULL 以 检 查 是 否 发 生 异 常 : /* a class in the Java programming language */ public class Window long handle; int length; int width; static native void initids(); static