• [其他] 自定义java函数在dws中执行报错:reflection/loadLibrary is not allowed
    # 问题现象: ``` 使用java自定义函数,数据库中执行报 reflection/loadLibrary is not allowed ``` # 问题分析 ``` 由于安全要求,数据库中java自定义函数部分功能默认关闭。 解决方案: 业务需要使用其他方法进行替换。 ``` ``` 参考:https://support.huaweicloud.com/devg-dws/dws_04_0936.html javaudf_disable_feature参数介绍:reflection,javaudf函数执行过程中禁用反射(ReflectPermission权限)。等注意事项 ``` # java自定义函数使用参考 ``` https://support.huaweicloud.com/devg-dws/dws_04_0509.html ```
  • [问题求助] 编译构建的Maven模板中没有我需要的版本号
    # 编译构建模板的版本号问题 我的代码使用的 JDK 是 17 的版本,maven 版本是 3.8.1 创建流水线的时候,编译构建的 Maven 模板中没有我需要的版本号。 请问这种情况,在不修改我项目代码中的 JDK 和 Maven 版本的条件下,还能使用流水线的编译构建吗?
  • [新手课堂] 鲲鹏DevKit调优助手
    # 【云驻共创】鲲鹏DevKit“0”门槛快速调优的秘密武器 鲲鹏DevKit调优助手通过系统化组织和分析性能指标、热点函数、系统配置等信息,形成系统资源消耗链条,引导用户根据优化路径分析性能瓶颈,并针对每条优化路径给出优化建议和操作指导,以此实现快速调优。解决客户软件运行遇到性能问题时凭人工经验定位困难,调优能力弱的痛点。 # 1、**鲲鹏DevKit介绍** 鲲鹏开发套件Kunpeng DevKit提供全栈开发工具,集代码迁移、编译调试、性能调优、异常诊断等工具和功能于一体。将开发者的工作各个环节一一串联,提供了一个方便、快捷和专业的工具包,通过鲲鹏DevKit可以帮助用户高效开发,一展宏图。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20224/20/1650421661908334803.png) # 2、**性能分析工具简介** 鲲鹏性能分析工具由四个子工具组成,分别为:系统性能分析、Java性能分析、系统诊断和调优助手。 系统性能分析是针对基于鲲鹏的服务器的性能分析工具,能收集服务器的处理器硬件、操作系统、进程/线程、函数等各层次的性能数据,分析系统性能指标,定位到系统瓶颈点及热点函数,并给出优化建议。该工具可以辅助用户快速定位和处理软件性能问题。 Java性能分析是针对基于鲲鹏的服务器上运行的Java程序的性能分析和优化工具,能图形化显示Java程序的堆、线程、锁、垃圾回收等信息,收集热点函数、定位程序瓶颈点,帮助用户采取针对性优化。 系统诊断是针对基于鲲鹏的服务器的性能分析工具,提供内存泄漏诊断(包括内存未释放和异常释放)、内存越界诊断、内存消耗信息分析展示、OOM诊断能力、网络丢包等,帮助用户识别出源代码中内存使用的问题点,提升程序的可靠性,工具还支持压测系统,如:网络IO诊断,评估系统最大性能。 调优助手是针对基于鲲鹏的服务器的调优工具,能系统化组织性能指标,引导用户分析性能瓶颈,实现快速调优。 其中,调优助手通过系统化组织和分析性能指标、热点函数、系统配置等信息,形成系统资源消耗链条,引导用户根据优化路径分析性能瓶颈,并针对每条优化路径给出优化建议和操作指导,以此实现快速调优。解决客户软件运行遇到性能问题时凭人工经验定位困难,调优能力弱的痛点。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20224/20/1650421675155605669.png) # 3、 性能分析工具应用场景 客户软件在基于鲲鹏的服务器上运行遇到性能问题时,可用系统性能分析来快速分析和定位。 系统性能分析工具将采集系统如下数据: - 系统软硬件配置和运行信息,例如:CPU类型、内存部署槽位、Kernel版本、内核参数、文件系统、系统运行日志参数等。 - 系统的CPU、内存、存储IO、磁盘IO等性能指标。 - 处理器PMU、SPE的性能数据。 - 处理器访问Cache/内存的次数、带宽、吞吐率等。 - 系统内核进行CPU资源调度、IO操作等数据。 - 进程/线程的CPU、内存、存储IO、上下文切换、系统调用等数据;进程命令行信息,包括:进程名、进程参数。 - 系统的热点函数及其调用栈;热点函数归属的程序/动态库(包含绝对路径);热点函数的汇编指令和热点指令;热点函数所对应的源代码(需要用户自行提供)。 客户Java应用软件在基于鲲鹏的服务器上运行遇到性能问题时,可用Java性能分析来快速分析和定位。 Java性能分析工具将采集如下数据: - Java进程运行环境信息,如:PID、JVM版本、JAVA版本、Main class、进程启动参数等。 - Java进程的CPU活动、内存占用、类加载信息、线程运行状态信息、系统的存储容量等。 - 可触发采集进程的堆转储信息,并获取堆转储中的类,实例,对象引用链等信息。 - Java进程的文件IO、SocketIO操作、数据库操作、HTTP请求、SpringBoot运行信息等。 - 对Java进程的方法、线程、内存、老年代对象、调用栈进行采样生成JFR采样文件。 客户软件在基于鲲鹏的服务器上运行遇到性能问题时,可用系统诊断来快速分析和定位。 系统诊断工具将采集系统如下数据: - 内存泄漏次数 - 内存泄漏大小 - 内存异常释放次数 - self内存泄漏 - 调用栈信息 - 系统的物理内存和虚拟内存大小 - 进程的内存MAP信息 - 应用的申请内存大小、申请次数、申请字节数、释放次数、释放字节数、泄漏次数以及泄漏字节数 - 分配器的分配内存、空闲内存、使用内存、Arena数、mmap区域数量以及mmap区域大小。 - 系统软硬件配置和运行信息,例如:CPU类型、内存部署槽位、Kernel版本、内核参数、文件系统、系统运行日志参数等。 - 系统的CPU、内存、存储IO、磁盘IO等性能指标。 - 处理器PMU、SPE的性能数据。 - 处理器访问Cache/内存的次数、带宽、吞吐率等。 - 系统内核进行CPU资源调度、IO操作等数据。 - 进程/线程的CPU、内存、存储IO、上下文切换、系统调用等数据;进程命令行信息,包括:进程名、进程参数。 - 系统的热点函数及其调用栈;热点函数归属的程序/动态库(包含绝对路径);热点函数的汇编指令和热点指令;热点函数所对应的源代码(需要用户自行提供)。 # 4、 实现原理 调优助手采集的数据覆盖OS、应用、硬件等系统各层的配置和性能指标,并根据硬件资源的消耗,来关联消耗这些硬件资源的软件信息,再从这些软件信息,来查看软件对其它的硬件资源消耗,从而推断出性能瓶颈。将数据从应用消耗、物理消耗以及硬件关联在一起。 - **Analysis Server**:实现性能数据分析及分析结果呈现。 | 表1 Analysis Server模块介绍 | | | --------------------------- | ------------------------------------------------------------ | | **模块名** | **功能** | | Web Browser | Web浏览器,用于操作交互和数据呈现。 | | Web Server | Web服务器,接收Web浏览器的请求,并触发Data Analysis Framework进行具体的业务处理。 | | Data Analysis Framework | 数据分析框架,主要作用是:o 通知Data Collection Framework进行数据采集,并接收采集的数据文件。o 调用相应的Data Analysis Plugin对数据文件进行入库和分析,并保存分析结果。o 为Web 服务器提供分析结果查询通道。 | | Data Analysis Plugin | 数据分析插件,不同性能分析功能有对应的分析插件,主要作用是:o 对数据文件进行预处理,并导入数据库中。o 分析原始数据,得出更适合展示的数据格式及数据间的关联关系,并结合以往项目调优经验值,给出优化建议。o 提供性能分析结果查询接口。 | - **Agent**:实现性能数据采集。 | 表2 Agent模块介绍 | | | ------------------------- | ------------------------------------------------------------ | | **模块名** | **功能** | | Data Collection Framework | 数据采集框架,主要作用是:o 接收Data Analysis Framework的采集通知,调用相应的Data Collection Plugin进行数据采集。o 将采集的数据文件推送给Data Analysis Framework。 | | Data Collection Plugin | 数据采集插件,不同性能分析功能有对应的采集插件,主要作用是完成具体的性能数据采集,并保存到文件。 | 说明: 鲲鹏性能分析工具只采集系统运行过程中的性能数据,不采集用户数据,不会造成客户信息泄露。
  • [活动公告] 【云享读书会第12期】《深入理解Java虚拟机》专家坐堂Q&A,还有礼品送不停哦~
    参与活动前请先报名活动:https://developer.huaweicloud.com/signup/fad4091322d542ef86abf830e5e6bb71 开发者,你好!欢迎来到 华为云 · 云享读书会 第12期 本期领读书籍为《深入理解Java虚拟机》由十年游戏行业经验、技术合伙人,技术博主, CSDN认证博客专家,2021年度华为云年度十佳博主,华为云享专家——香菜,带你精读这本JVM高级特性与最佳实践经过视频领读,如果你有相关知识收获,欢迎在此帖提问互动~活动时间2022.04.26 - 2022.05.20回复内容要求1.可以提出您在课程学习中产生的任何疑问或者问题,也可以是您产生的深刻思考,我们统统欢迎;2.禁止抄袭或复制读书笔记帖的任何内容;3.您也可以参考以下互动话题,与领读专家和其他同学互动交流;4.回帖时请务必留下你的微信昵称和华为云账号。互动话题请说说您在本次课程中的收获注意事项1.小助手会在活动结束后按续完成审核,并增加活动积分10活动积分/人,优质回复额外增加10或20活动积分/人;2.本次活动通过参与专家坐堂Q&A,可获得的积分上限为50分;3.请务必按照上述要求提交内容,以免影响积分增加;4.若积分值相同则以完成学习任务的时间先后排序,其中任务完成时间的判定优先级为:读书笔记>自测题>专家Q&A>其他;5.其他积分获取方式请查看活动社群公告;6.其他未说明事项请参照:云享读书会 第12期 《深入理解Java虚拟机》最终积分排名1-5名最终积分排名6-20名关于云享读书会每期云享读书会活动,会选取一本技术相关的畅销书籍,由原作者/行业专家提炼书籍精华,在读书会的专属微信社群中,每日输出精华知识的领读视频,帮助大家快速积累专业知识。活动期间会设置每日自测题、结业实践任务、提交读书笔记三种积分获取任务,并根据活动结束后的积分排行发放活动奖励。、加入学习社群添加小助手微信,回复“虚拟机”加入学习社群。
  • [活动公告] 【云享读书会第12期】《深入理解Java虚拟机》读书笔记征集,更有礼品相送~
    参与活动前请先报名活动:https://developer.huaweicloud.com/signup/fad4091322d542ef86abf830e5e6bb71开发者,你好!欢迎来到 华为云 · 云享读书会 第12期 本期领读书籍为《深入理解Java虚拟机》由十年游戏行业经验、技术合伙人,技术博主, CSDN认证博客专家,2021年度华为云年度十佳博主华为云享专家——香菜,带你精读这本JVM高级特性与最佳实践经过视频领读,如果你有相关知识收获,欢迎在此帖留下你的读书笔记~征集时间2022.04.19 - 2021.05.20读书笔记要求1. 每篇读书笔记字数要求≥300字;2. 内容要求与领读视频、领读书籍或是其他Java虚拟机相关;3.前往 博客版块 以 【读书会第十二期】+标题 形式发布博文;4. 内容原创不可抄袭;5. 回帖时留下你的微信昵称和华为云账号+博文链接。注意事项1. 读书笔记提交后,小助手会按续完成审核,并增加活动积分50活动积分/篇;2. 本次活动通过提交读书笔记,可获得的积分上限为200分;3. 请务必按照上述要求提交内容,以免影响积分增加;4. 若积分值相同则以完成学习任务的时间先后排序,其中任务完成时间的判定优先级为:读书笔记>自测题>专家Q&A>其他;5. 其他积分获取方式请查看活动社群公告;6. 其他未说明事项请参照:云享读书会 第12期 《深入理解Java虚拟机》最佳读书笔记奖励针对活动时间内提交的有效读书笔记,专家将根据内容质量和完成篇数综合评选1位最佳读书笔记获奖者,奖励 猫王收音机 胡桃木最终积分排名1-5名最终积分排名6-20名关于云享读书会每期云享读书会活动,会选取一本技术相关的畅销书籍,由原作者/行业专家提炼书籍精华,在读书会的专属微信社群中,每日输出精华知识的领读视频,帮助大家快速积累专业知识。活动期间会设置每日自测题、结业实践任务、提交读书笔记三种积分获取任务,并根据活动结束后的积分排行发放活动奖励。加入学习社群添加小助手微信,回复“虚拟机”加入学习社群。
  • [技术干货] 详细教程:应用侧开发Java Demo
    华为云IOT的应用侧开发Java Demo使用详细教程(IDEA 开发)第0章 简介        最近有很多小伙伴在使用华为云IOT的应用侧开发的Java Demo时遇到一些问题,本期就教大家如何使用这个基于华为云官方提供的Java Demo,本期教程带大家做的是查询的在线状态和设备影子属性,并解析JSON数据,大家可以根据自己的需要进行二次开发,结果如下:有需要参考esp8266系列设备接入华为云物联网平台(IOTDA) 并完成设备属性上报流程的小伙伴可以参考我之前发的帖子。第1章 整体流程概述    一、操作前的准备    二、导入华为云官方提供的应用侧开发Java Demo工程并配置环境    三、获取Token    四、调用接口第2章 具体详细步骤一、 操作前的准备 1、环境准备  (1) JDK(jdk-8u161-windows-x64.exe+配置环境变量)  (2) IntelliJ IDEA  (3) apache-maven  (4) 设备接入IOTDA 2、 参数信息准备:进入我的凭证页 准备下面信息(样例,请大家换成自己的参数,不清楚的可以参考下面的“参数来源”)  (1) 华为云账号名:hw_666666_01  (2) IAM用户名:FUNIOT(一般默认为账号名,此处我采用的是创建的其他IAM账户:FUNIOT)  (3) IAM用户登录华为云密码:ABCD1234  (4) 项目ID:09903251671035vz9u03030f  (5) 设备ID:513423536g4be_esp8266_test01  (6) 设备属性:temp    (本次教程用于查询设备影子的设备属性)  (7) IAM服务对接地址:https://iam.cn-north-4.myhuaweicloud.com     (根据自己项目调整 )  (8) 接入服务对接地址:https://iotda.cn-north-4.myhuaweicloud.com (根据自己项目调整 ) 3、 【参数来源】  (1) 设备ID、设备属性、产品ID可在设备接入控制台 中查看  (2) IAM:默认为华为云账号名,根据官方提示,如果您的华为云帐号已升级为华为帐号,不支持获取帐号Token,建议您在统一身份认证 处创建IAM用户,授予该用户权限,以获取该IAM用户的Token。二、 导入华为云官方提供的应用侧开发Java Demo工程并配置环境 1、 下载官方提供的Java Demo          链接:https://iot-developer.obs.cn-north-4.myhuaweicloud.com/javaApiDemo2.zip            此时我们得到了javaApiDemo2.zip的压缩文件,然后接下来我们解压缩文件并导入工程。 2、 导入工程并进行简单配置   (1) 导入工程:Open->路径\pom.xml->Open as Project   (2) 配置工程的本地maven仓库     File->Settings->Bulid,Execution,Deployment->Maven->User setting file,选择maven的config->setting.xml     注:一般设置好User setting file 路径后Local respository会自动更新三、 获取Token 1、 配置IAM服务对接地址和接入服务对接地址    在IDEA中,打开“JavaApiDemo> src >main>java> com.huawei.util > Constants.java”,修改TOKEN_BASE_URL、IOTDM_BASE_URL,将我们提前准备的地址数据填入: 2、 获取IAM用户Token接口鉴权  (1) 打开JavaApiDemo -> src -> main ->java ->com.huawei.demo.auth -> Authentication.java  在88行左右填入我们设置的账号名、IAM用户名、IAM用户密码 (2) 右键运行“Authentication.main()”,查看控制台的输出结果,如果出现很长的字符串,意味着鉴权成功。  注意:这个不需要大家复制保存,系统会生成一个token.text文件,程序中每次获取token时都会优先从文件中获取之前保存的token,如果token失效(有效期为24小时),代码会重新获取新的token并保存到token.text文件中。四、 调用接口 1、 查询设备  (1) 查看查询设备的URI:我们在官方提供的API参考文档中可以看到 (2) 添加URI:  我们打开之前配置IAM接入地址的Constants.java文件,路径:JavaApiDemo> src >main>java> com.huawei.util > Constants.java文件,加入/修改下面这句:public static final String DEVICE_COMMAND_URL = IOTDM_BASE_URL + "/v5/iot/%s/devices/%s";(3) 调用接口   (a) 修改参数打开JavaApiDemo > src > main>java>com.huawei.demo.device > QueryDeviceList.java文件,将我们事先准备的相关参数填入后,右键运行“QueryDeviceList.main()”    (b) 查看运行结果    (c) JSON解析(我使用的是开源中国社区的 J SON 格式化显示工具 )    为了方便观看和分析,我们将上面得到的数据复制下来,进行json格式化一下教程中我们需要的是查询设备在线状态,我们查看JSON数据可以发现”status”:”OFFLINE”是我们需要的,接下来我们进行解析:参考代码ObjectMapper objectMapper = new ObjectMapper(); JsonNode jsonNode = objectMapper.readValue(getContent, JsonNode.class); JsonNode statusNode = jsonNode.get("status"); String statusstr = statusNode.asText(); System.out.println("status = " + statusstr); if(statusstr.equals("OFFLINE")) System.out.println("设备离线"); else if(statusstr.equals("ONLINE")) System.out.println("设备在线");【运行结果】为了方便测试,直接使用MQTT.fx进行登陆,我们再次运行程序会发现已经查询到设备在线2、 查询设备影子-属性(1) 查看查询设备的URI:我们在官方提供的API参考文档中可以看到(2) 添加URI:我们再次打开之前配置IAM接入地址的Constants.java文件,路径:JavaApiDemo> src >main>java> com.huawei.util > Constants.java文件加入/修改下面这句:public static final String DEVICE_SHADOW_URL = IOTDM_BASE_URL + "/v5/iot/%s/devices/%s/shadow";(3) 调用接口  (a) 修改参数打开JavaApiDemo > src > main>java>com.huawei.demo.device > QueryDeviceList.java文件,将我们事先准备的相关参数填入后,右键运行“QueryDeviceList.main()”(b) 查看运行结果(c) JSON解析同样,我们将上面得到的数据复制下来,进行json格式化一下教程中我们需要的是查询设备影子的属性,我们查看JSON数据可以发现”temp”:”22”是我们需要的,接下来我们进行解析:参考代码//解析json 例2:查询设备影子 temp属性 ObjectMapper objectMapper = new ObjectMapper(); JsonNode jsonNode = objectMapper.readValue(getContent, JsonNode.class); JsonNode tempNode = jsonNode.get("shadow").get(0).get("reported").get("properties").get("temp"); String tempstr = tempNode.asText(); System.out.println("temp = " + tempstr); System.out.println("温度:" + tempstr+"℃");【运行结果】与控制台查看的数据一致到这里大家就已经掌握了JAVA Demo的基本操作,大家可以试着写一个界面程序进行显示,我写了一个如文章开头所示的界面,bug很多,勉强能用,论坛里大佬很多,就不在这张贴了,有需要的小伙伴可以关注微信公众号“IOT趣制作”,回复关键字“华为云应用java”获取测试界面程序,然后直接加到JavaApiDemo > src > main>java>com.huawei.demo.device >目录下,运行main()即可。【参考文档及链接】华为云官方Java Demo使用说明:https://support.huaweicloud.com/devg-iothub/iot_02_3002.html 创建IAM用户教程:https://support.huaweicloud.com/usermanual-iam/iam_02_0001.html 华为云API列表:https://support.huaweicloud.com/api-iothub/iot_06_v5_0003.html 构造请求说明:https://support.huaweicloud.com/api-iothub/iot_06_v5_0090.html 认证鉴权说明:https://support.huaweicloud.com/api-iothub/iot_06_v5_0091.html 开源中国社区的JSON格式化显示工具:https://tool.oschina.net/codeformat/json/ esp8266系列设备接入华为云物联网平台(IOTDA):https://bbs.huaweicloud.com/forum/forum.php?mod=viewthread&tid=179090
  • [问题求助] Java Demo报错
    新人,下了手册的java demo,也跟着教程一步一步做,做到这个获取token时,参数都按教程改了,运行后给我的报错是please check your network,这是为什么啊,网络连接是哪里有问题吗?这是官网手册的步骤以及运行结果然后这是我的运行结果
  • [问题求助] java jnr/jni调用libfuse实现用户文件系统问题
    【功能模块】java jnr/jni调用libfuse实现了用户文件系统,在x86的环境下可以成功挂载,在华为云的kunpeng服务器(centos7操作系统)无法挂载成功。【操作步骤&问题现象】1、附件fsfuse.zip上传到kunpeng服务器,unzip fsfuse.zip解压。2、kunpeng服务器安装libfuse:yum install fuse fuse-libs3、mkdir /tmp/mnth4、java -cp fsmount.jar ru.serce.jnrfuse.examples.HelloFuse5、ll /tmp 发现mnth挂载成功,但是显示权限和用户组信息有问题。【截图信息】【日志信息】(可选,上传日志内容或者附件)
  • [技术干货] JVM-垃圾回收算法和垃圾回收器[转载]
    一、GC-垃圾回收:stop-the-world(stw): 他会在任何一种GC算法中发生。stw意味着jvm因为需要执行GC而停止了应用程序的执行。当stw发生时,出GC所需的线程外,所有的线程都进入等待状态,直到GC任务完成。GC优化的很多时候,就是减少stw的发生。需要注意的是,jvm gc只回收堆和方法区内的对象,而栈区的数据,在超出作用域后会被jvm自动释放掉,所有其不再jvm gc的管理范围内。jvm -gc 如何判断对象可以被回收了?对象没有应用作用域发生未捕获异常程序在作用域正常执行完毕程序执行了System.exit();程序发生意外终止(被杀线程等)在java程序中不能显示的分配和注销缓存,因为这些事情jvm都帮我们做了,那就是GC.有些时候我们可以将相关对象设置成null来试图显示的清楚缓存,但是并不是设置成null就会一定被标记为可回收,有可能会发生逃逸。将对象设置成null至少没有什么坏处,但是使用System.gc()便不可取了,使用System.gc()的时候并不是马上执行GC操作,而是会等待一段时间,甚至不执行,而且System.gc()如果别执行,会出发Full GC,这费城影响性能。GC什么时候执行:eden区空间不够存放新对象的时候,执行minor gc。 升到年老代的对象大于老年代的剩余空间时执行full gc,或者小于的时候,被 HandlePromotionFailure 参数强制Full GC。 调优主要是减少Full GC 的触发次数,可以通过NewRatio 控制新生代转老年代的比例,通过MaxTurningThreshold 设置对象进入老年代的年龄阀值。按代的垃圾回收机制:新生代(Young generation):绝大多数的最新被创建的对象都会被分配到这里,由于大部分在创建后很快变得不可达,很多对象别创建在新生代,然后消失。对象从这个区域消失的过程,我们称之为 Minor GC老年代(old generation): 对象没有变得不可达,并且从新生代周期中存活了下来,会被拷贝到这里。其区域分配的空间要比新生代多。也正是由于其相对较大的空间,发生在老年代的GC次数要比新生代少得多。对象从老年代消失的过程称之为: Major GC, 或者 Full GC.持久代(Permanent generation):也称之为方法区,用于保存类常量以及字符串常量,注意,这个区域不是用于存储那些从老年代存活下来的对象,这个区域也可能发生GC, 发生在这个区域的GC事件也被算作Major GC,只不过在这个区域发生GC 的条件非诚严苛,必须符合以下三种条件:所有实例被回收加载该类的ClassLoader被回收Class 对象无法通过任何途径访问(包括反射)如果老年代要引用新生代的对象,会发生什么呢?为了解决这个问题,老年代中存在一个 card table ,它是一个512byte大小的块。所有老年代的对象指向新生代对象的引用都会被记录在这个表中。当针对新生代执行GC的时候,只需要查询 card table 来决定是否可以被回收,而不用查询整个老年代。这个 card table 由一个write barrier 来管理。write barrier给GC带来了很大的性能提升,虽然由此可能带来一些开销,但完全是值得的。默认的新生代和老年代所占空间的比例为1:2新生代空间的构成和逻辑:分为三个部分: 一个伊甸园空间(eden), 两个幸存者空间)(From Survivor, To Survivor)默认比例: Eden:From:to = 8:1:1每个空间执行顺序:绝大多数刚刚被创建的对象会存放在伊甸园EDEN空间在eden空间执行第一次gc(minor gc)后,存活的对象被移动到其中的一个幸存者区此后,每次Eden空间执行gc后,存活的对象都会被堆积在同一个幸存者空间。当一个幸存者空间饱和,还存在存活的对象会被移动到另一个幸存者空间,然后会清空已经饱和的那个幸存者空间在以上步骤中重复N次(N=MAXTenuringThreshold(年龄阀值设定,默认15))依然存活的对象,就会别移动到老年代从上面的步骤可以发现,两个幸存者空间,必须有一个是保持空的,如果两个幸存者空间都有数据,或者两个都是空的,那一定是你的系统出现了某种错误。我们需要重点记住的是,对象在刚刚被创建之后,是保存在Eden区的,哪些长期存活的对象会经由幸存者空间转到老年代空间。也有例外的情况,对于一些比较大的对象(需要分配连续比较大的空间)则直接进入到老年代,一般在幸存者空间不足的情况下发生。老年代空间的构成与逻辑:老年代空间的构成其实很简单,他不像新生代那样划分为几个区域,他只有一个区域,里面存储的对象并不像新生代空间绝大部分都是朝闻道,夕死矣。这里的对象几乎都是从Survivor空间中熬过来的,他们绝不会轻易狗带。因此FULL GC 发生的次数不会有minor gc那么频繁,并且做一次full gc的时间比minor gc要更长(约10倍)二、GC算法:1. 根搜索算法(可达性分析):从GCROOT开始,寻找对应的引用节点,找到这个节点后,继续寻找这个节点的引用节点。当所有的引用节点寻找完毕后,剩余的节点则被认为是没有被引用到的节点,及无用的节点。目前java中可以作为GCroot的对象有: 虚拟机栈中引用的对象(本地变量表),方法区中静态属性引用的对象,方法区中常量引用的对象,本地方法栈中引用的对象(native)2. 标记-清除算法:标记-清除算法采用从根集合进行扫描,对存活的对象进行标记,标记完毕后,在扫描整个空间中未标记的对象进行直接回收。标记-清除算法不需要进行对象的移动,并且仅对不存活的对象进行处理,在存活的对象比较多的情况下极为高效,但是由于标记-清除算法直接回收不存活的对象,并没有对存活的对象进行整理,因此会导致内存碎片。3. 复制算法:复制算法将内存划分为两个区间,使用此算法时,所有的动态分配的对象都只能分配在其中一个区间,而另一个区间是闲置的。复制算法采用从根集合扫描,将存活对象复制到空闲区间,当扫描完毕活动区间后,会将活动区间一次性全部回收,此时原本的空闲区间变成了活动区间,下次gc的时候会重复刚才的操作,以此循环。复制算法在存活对象较少的时候,极为高效,但是带来的成本是牺牲一半的内存空间用于对象的移动,所以复制算法使用的场景,必须是对象的存活率非常低才行。4. 标记-整理算法:标记-整理算法采用和标记-清除算法一样的方式进行对象的标记,清除,但是在回收不存活对象占用的空间后,会见给所有的存活的对象往左端空闲空间移动,并更新对应的指针。标记-整理算法是在标记-清除算法之上,又进行了对象的移动排序整理,因此成本更高,但却解决了内存碎片的问题,JVM 为了优化内存得回收,是用来分代回收的方式,对于新生代的内存回收,主要采用复制算法,而对于老年代的回收,大多采用标记整理算法。三、垃圾回收器需要注意的是,每一个回收器都存在stw的问题,只不过各个回收器在stw时间优化程度、算法的不同,可根据自身需求选择适合的回收器。1.Serial(-XX: + UseSerialGC)从名字可以看出,这是一个串行的垃圾回收器,这也是java虚拟机中最基本,历史最悠久的收集器,在jdk1.3之前是java虚拟机新生代收集器的唯一选择,目前也是ClientVM 下ServerVM4核4gb以下机器的默认垃圾回收器,Serial收集器并不是只能使用一个CPU进行收集,而是当jvm需要进行垃圾回收的时候,需暂停所有的用户线程,直到回收结束。使用算法: 复制算法。Serial收集器虽然是最老的,但是它对于限定单个CPU的环境来说,由于没有线程交互的开销,专心做垃圾收集,所以它在这种情况下是相对于其他收集器中最高效的。2. SerialOld(-XX: + UseSerialGC)SerialOld是Serial收集器的老年代收集器版本,它同样是一个单线程收集器,这个收集器目前主要用于Client模式下使用。如果在Server模式下,它主要还有两大用途:一个是在JDK1.5及之前的版本中与Parallel Scavenge收集器搭配使用,另外一个就是作为CMS收集器的后备预案,如果CMS出现Concurrent Mode Failure,则SerialOld将作为后备收集器。使用算法:标记 - 整理算法3. ParNew(-XX: +UseParNewGC)ParNew其实就是Serial收集器的多线程版本。除了Serial收集器外,只有它能与CMS收集器配合工作。使用算法: 复制算法ParNew 是许多运行在Server模式下的JVM的首选的新生代收集器,但是在单cpu的情况下,他的效率远远低于Serial收集器,所以一定要注意使用场景。4. ParallelScavenge(-XX:+UseParallelGC)ParallelScavenge又被称为吞吐量优先收集器,和ParNew 收集器类似,是一个新生代收集器使用算法: 复制算法ParallelScavenge收集器的目的是打到一个可控的吞吐量,所谓吞吐量就是cpu用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)。如果虚拟机总共运行了100分钟,其中垃圾回收用了1分钟,那么吞吐量就是99%所以这个收集器适合在后台运算而不需要很多交互的任务。接下来看看两个用于准备控制吞吐量的参数 1,-XX:MaxGCPauseMills(控制最大垃圾收集的时间) 设置一个大于0的毫秒数,收集器尽可能地保证内存回收不超过设定值。但是并不是设置地越小就越快。GC停顿时间缩短是以缩短吞吐量和新生代空间来换取的。 2,-XX:GCTimeRatio(设置吞吐量大小) 设置一个0-100的整数,也就是垃圾收集时间占总时间的比率,相当于吞吐量的倒数。5. ParallelOld(-XX: + UseParallelOldGC)ParallelOld 是并行收集器,和SerialOld 一样,是一个老年代收集器,是老年代吞吐量优先的一个收集器,这个收集器在JDK1.6之后才开始提供的,再次之前,ParallelScavenge只能选择SerialOld来作为其老年代的收集器,这严重拖累了ParallelScavenge的整体速度,而ParallelOld出现了之后,吞吐量有限收集器才名副其实使用算法: 标记-整理算法在注重吞吐量与CPU数量大于1 的情况下,都可以优先考虑ParallelScavenge + ParallelOld收集器6. CMS(-XX:UseConcMarkSweepGC)CMS是一个老年代收集器,全称Concurrent Low Pause Collector, 是JDK1.4以后开始引用的心GC收集器,在jdk5,jdk6中得到了进一步的改进。他是对于响应时间的重要性需求大于吞吐量要求的收集器,对于要求服务器响应速度高的情况下,使用CMS非常合适。CMS的一大特点,就是用两次短暂的暂定来代替串行或者并行标记整理算法时候的长暂停使用算法:标记-清理执行过程如下:初始标记(STW initial mark):在这个阶段,需要虚拟机停顿在正在执行的应用线程,官方叫法叫做STW,这个过程从根对象扫描直接关联的对象,并做标记,这个过程会很快完成。并发标记(Concurrent marking) :这个阶段紧随初始标记阶段,在初始标记的基础上继续向下追溯标记,注意这里是并发标记,标识用户线程可以和GC线程一起并发执行,这个阶段不会暂停用户线程并发预清理(Concurrent precleaning):这个阶段仍然是并发的,jvm查找正在执行并发标记阶段时候进入老年代的对象(可能这是会有对象从新生代晋升到老年代,或被分配到老年代)通过重新扫描,减少在一个阶段重新标记的工作,因为下一个阶段会stw重新标记(stw remark): 这个阶段会再次暂停正在执行的应用线程,重新从根对象开始查找并标记并发阶段遗漏的对象(在并发标记阶段结束后对象状态的更新导致)并处理对象关联,这一次耗时回避“初始标记”更长,并且这个阶段可以并行标记。并发清理(Concurrent sweeping): 这个阶段是并发的,应用程序和GC清理线程可以一起并发执行并发重置(Concurrent reset):这个阶段仍然是并发的,重置CMS收集器的数据结构,等待下一次垃圾回收CMS:缺点:内存碎片;由于使用了标记-清理算法,导致内存空间中会产生内存碎片,不过CMS收集器做了一些小的优化,就是把未分配的空间汇总成一个列表,当有JVM需要分配内存空间的时候,会搜索这个列表找到符合条件的空间来存储这个对象,但是内存碎片的问题仍然存在,如果一个对象需要三块连续的空间来存储,因为内存碎片的问题,找不到这样的空间,就会导致full gc.需要更多的CPU资源:由于使用了并发处理,很多情况下都是GC线程和用户线程并发执行的,这样就需要占用更多的CPU资源,也是牺牲了一定吞吐量的原因。需要更大的堆空间:因为CMS标记阶段用用程序的线程还是执行的,那么就会有堆空间继续分配的问题,为了保障CMS在回收堆空间之前还有空间分配给新加入的对象,必须预留一部分空间,cms默认在老年代空间使用68%的时候启动垃圾回收,可以通过-XX:CMSinitiatingOccupancyFraction=n来设置这个阀值。7. garbageFirst(G1)G1收集器是jdk1.7提供的一个新的收集器,是当今收集器技术发展的最前沿成果之一。G1是一款面向服务端应用的垃圾收集器,Hotspot开发团队赋予它的使命是未来可以替换掉cms.G1具备以下特点:并行与并发: G1能充分利用多CPU,多核心环境下的硬件优势,使用多个CPU来缩短STW停顿的时间,部分其他收集器原本需要停顿java线程执行的G1动作,G1收集器仍然可以通过并发的方式让java程序继续执行。分代收集:与其他收集器一样,分代概念在G1中仍然得以保留,虽然G1可以不需要其他收集器配合就能单独管理整个GC堆,但他能够采取不同的方式去处理新创建的对象和已经存活了一段时间。熬过多个gc的旧对象已获得更好的收集效果空间整合:与CMS的标记-清除算法不同,G1收集器从整体上看是基于标记-整理算法实现的,从局部(两个region)上看是基于复制算法实现的,但无论如何,两种算法都意味着g1运行期间不会产生内存空间碎片,收集后能够提供规整的可用内存。这种特性有利于程序的长时间运行, 分配大对象时不会因为无法找到连续的内存空间而提前触发下一次GC可预测的停顿:这是G1相比cms的另一大优势,降低停顿时间是G1和cms的共同关注点,但是G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间段内,消耗在垃圾收集上的时间不得超过N毫秒,这几乎已经是实时java(RTSJ)的垃圾收集器的特征了。整理一下新生代和老年代的收集器。新生代收集器:Serial (-XX:+UseSerialGC)ParNew(-XX:+UseParNewGC)ParallelScavenge(-XX:+UseParallelGC)G1 收集器老年代收集器:SerialOld(-XX:+UseSerialOldGC)ParallelOld(-XX:+UseParallelOldGC)CMS(-XX:+UseConcMarkSweepGC)G1 收集器
  • [新手课堂] Java的实现
    // Bubble sort in Javaimport java.util.Arrays;class Main {  // perform the bubble sort  static void bubbleSort(int array[]) {    int size = array.length;        // loop to access each array element    for (int i = 0; i < size - 1; i++)          // loop to compare array elements      for (int j = 0; j < size - i - 1; j++)        // compare two adjacent elements        // change > to < to sort in descending order        if (array[j] > array[j + 1]) {          // swapping occurs if elements          // are not in the intended order          int temp = array[j];          array[j] = array[j + 1];          array[j + 1] = temp;        }  }  public static void main(String args[]) {          int[] data = { -2, 45, 0, 11, -9 };        // call method using class name    Main.bubbleSort(data);        System.out.println("Sorted Array in Ascending Order:");    System.out.println(Arrays.toString(data));  }}
  • [交流分享] 计算机为何可以运行Java代码?
    Java代码有很多种不同的运行方式。比如说可以在开发工具中运行,可以双击执行jar文件运行,也可以在命令行中运行,甚至可以在网页。这些执行方式都离不开JRE,Java运行时环境。JRE仅包含运行Java程序的必需组件,包括Java虚拟机以及Java核心类库等。我们Java程序员经常接触到的JDK(Java开发工具包)同样包含了JRE,并且还附带了一系列开发、诊断工具。然而,运行C++代码则无需额外的运行时。往往把这些代码直接编译成CPU所能理解的代码格式,即机器码。比如下图的中间列,就是用C语言写的Helloworld程序的编译结果。C程序编译而成的机器码就是一个个的字节,它们是给机器读的。那为让开发人员也能理解,用反汇编器将其转换成汇编代码(如下图的最右列所示)。; 最左列是偏移;中间列是给机器读的机器码;最右列是给人读的汇编代码0x00:  55                    push   rbp0x01:  48 89 e5              mov    rbp,rsp0x04:  48 83 ec 10           sub    rsp,0x100x08:  48 8d 3d 3b 00 00 00  lea    rdi,[rip+0x3b]                                     ; 加载"Hello, World!\n"0x0f:  c7 45 fc 00 00 00 00  mov    DWORD PTR [rbp-0x4],0x00x16:  b0 00                 mov    al,0x00x18:  e8 0d 00 00 00        call   0x12                                    ; 调用printf方法0x1d:  31 c9                 xor    ecx,ecx0x1f:  89 45 f8              mov    DWORD PTR [rbp-0x8],eax0x22:  89 c8                 mov    eax,ecx0x24:  48 83 c4 10           add    rsp,0x100x28:  5d                    pop    rbp0x29:  c3                    ret为什么Java要在虚拟机里运行?Java作为一门高级程序语言,它的语法非常复杂,抽象程度也很高。因此,直接在硬件上运行这种复杂的程序并不现实。所以呢,在运行Java程序之前,需要对其进行一番转换。转换怎么操作设计一个面向Java语言特性的虚拟机,并通过编译器将Java程序转换成该虚拟机所能识别的指令序列,即Java字节码。之所以这么取名,是因为Java字节码指令的操作码(opcode)被固定为一个字节。下图的中间列,正是用Java写的Helloworld程序编译而成的字节码。可以看到,它与C版本的编译结果一样,都是由一个个字节组成的。同样可以将其反汇编为人类可读的代码格式(如下图的最右列所示)。Java版本的编译结果相对精简一些,Java虚拟机相对于物理机而言,抽象程度更高。# 最左列是偏移;中间列是给虚拟机读的机器码;最右列是给人读的代码0x00:  b2 00 02         getstatic java.lang.System.out0x03:  12 03            ldc "Hello, World!"0x05:  b6 00 04         invokevirtual java.io.PrintStream.println0x08:  b1               returnJava虚拟机常见的是在各个现有平台(如Windows_x64、Linux_aarch64)上提供软件实现。一旦一个程序被转换成Java字节码,便可在不同平台上的虚拟机实现里运行,即“一次编写,到处运行”。虚拟机的另外一个好处是它带来托管环境(Managed Runtime),代替我们处理一些代码中冗长而且容易出错的部分。自动内存管理与垃圾回收,这部分内容甚至催生了一波垃圾回收调优。托管环境还提供了诸如数组越界、动态类型、安全权限等等的动态检测。Java虚拟机具体是怎样运行Java字节码的?以标准JDK中的HotSpot虚拟机为例,从虚拟机以及底层硬件两个角度,给你讲一讲Java虚拟机具体是怎么运行Java字节码的。虚拟机视角,执行Java代码首先要将它编译而成的class文件加载到Java虚拟机。加载后的Java类会被存放于方法区(Method Area)。实际运行时,虚拟机会执行方法区内的代码。这和段式内存管理中的代码段类似。而且,Java虚拟机同样也在内存中划分出堆和栈来存储运行时数据。但Java虚拟机会将栈细分为面向Java方法的Java方法栈,面向本地方法(用C++写的native方法)的本地方法栈,以及存放各个线程执行位置的PC寄存器。运行过程中,每当调用进入一个Java方法,Java虚拟机会在当前线程的Java方法栈中生成一个栈帧,存放局部变量以及字节码的操作数。这个栈帧的大小是提前计算好的,而且Java虚拟机不要求栈帧在内存空间里连续分布。当退出当前执行的方法时,不管是正常返回、异常返回,Java虚拟机均会弹出当前线程的当前栈帧,并舍弃。硬件视角,Java字节码无法直接执行。因此,Java虚拟机需要将字节码翻译成机器码。HotSpot翻译过程有两种形式:解释执行,逐条将字节码翻译成机器码并执行无需等待编译即时编译(Just-In-Time compilation,JIT),将一个方法中包含的所有字节码编译成机器码后再执行实际运行速度更快HotSpot默认采用混合模式,综合了解释执行和即时编译两者的优点:先解释执行字节码,而后将其中反复执行的热点代码,以方法为单位进行即时编译。Java虚拟机的运行效率HotSpot采用了多种技术来提升启动性能以及峰值性能,即时编译便是其中最重要的技术之一。即时编译建立在程序符合二八定律,百分之二十的代码占据了百分之八十的计算资源。对占据大部分的不常用的代码,无需耗费时间将其编译成机器码,而是采取解释执行的方式运行对于仅占据小部分的热点代码,我们则可以将其编译成机器码,以达到理想运行速度。理论即时编译后的Java程序的执行效率,是可能超过C++。因为与静态编译相比,即时编译拥有程序的运行时信息,并且能够根据这个信息做出相应的优化。虚方法是用来实现多态性。对一个虚方法调用,尽管有很多目标方法,但实际运行过程中,可能只调用其中一个。这信息可被即时编译器所利用,规避虚方法调用的开销,达到比静态编译的C++程序更高的性能。为满足不同用户场景的需要,HotSpot内置了多个即时编译器:C1、C2和Graal。Graal是Java 10正式引入的实验性即时编译器。之所以引入多个即时编译器,为在编译时间和生成代码的执行效率之间进行取舍。C1又叫做Client编译器,面向对启动性能有要求的客户端GUI程序,采用的优化手段相对简单,因此编译时间较短。C2又叫做Server编译器,面向对峰值性能有要求的服务器端程序,采用的优化手段相对复杂,因此编译时间较长,但同时生成代码的执行效率较高。从Java 7开始,HotSpot默认采用分层编译的方式:热点方法首先会被C1编译,而后热点方法中的热点会进一步被C2编译。为了不干扰应用的正常运行,HotSpot的即时编译是放在额外的编译线程中进行的。HotSpot会根据CPU的数量设置编译线程的数目,并且按1:2的比例配置给C1及C2编译器。在计算资源充足的情况下,字节码的解释执行和即时编译可同时进行。编译完成后的机器码会在下次调用该方法时启用,以替换原本的解释执行。总结在虚拟机中运行,是因为它提供了可移植性。一旦Java代码被编译为Java字节码,便可以在不同平台上的Java虚拟机实现上运行。此外,虚拟机还提供了一个代码托管的环境,代替我们处理部分冗长而且容易出错的事务,例如内存管理。Java虚拟机将运行时内存区域划分为五个部分,分别为方法区、堆、PC寄存器、Java方法栈和本地方法栈。Java程序编译而成的class文件,需要先加载至方法区中,方能在Java虚拟机中运行。为了提高运行效率,标准JDK中的HotSpot虚拟机采用的是一种混合执行的策略。它会解释执行Java字节码,然后会将其中反复执行的热点代码,以方法为单位进行即时编译,翻译成机器码后直接运行在底层硬件之上。HotSpot装载了多个不同的即时编译器,以便在编译时间和生成代码的执行效率之间做取舍。
  • [安装] MindSpore Lite 1.6.1版本官方文档关于java环境下版本获取方式存在错误
    依旧提供的是1.5.0版本包导入方式https://mindspore.cn/lite/docs/zh-CN/r1.6/use/runtime_java.html#id11
  • [java] 【华为matepad 11】【硬件aec】华为matepad 11硬件aec的效果非常差
    【功能模块】音频模块【操作步骤&问题现象】1、加入会议,远端播放声音2、本端华为matepad 11使用硬件aec3、远端听到回声,硬件aec完全没处理掉回声4、抓去的系统log,显示是有硬件aec音效的:effects client='Acoustic Echo Canceler'5、使用java接口判断是否支持硬件aec,也是返回支持硬件aec(代码见下边截图)【问题点】1、从最终的结果来看,华为matepad 11明明不支持硬件aec(效果非常差),为何调用系统接口返回的是true?2、如果不是从代码中的接口获取是否支持硬件aec,那应该如何正确的判断安卓设备是否支持硬件aec?3、从目前的测试结果来看,非常多的安卓机型存在以下问题:明明不支持硬件aec(效果非常差),但是调用系统接口却返回支持硬件aec硬件aec无法关闭,调用java接口关闭硬件aec后,硬件aec仍然在生效(因为麦克风返回的数据已经没有回声或者回声被处理过一些但仍有残留)import android.media.audiofx.AcousticEchoCanceler;AcousticEchoCanceler aec = AcousticEchoCanceler.create(audioSession);aec.setEnabled(false);【截图信息】
  • [新手课堂] 用java写Student类的功能
    1)编写一个Java程序片断,以定义一个表示学生的类Student。这个类的属性有“学号”、“班号”、“姓名”、“性别”、“年龄”,方法有“获得学号”、“获得班号”、“获得性别”、“获得姓名”、“获得年龄”。2)为类Student增加一个方法public String toString( ),该方法把Student类的对象的所有属性信息组合成一个字符串以便输出显示。编写一个Java Application程序,创建Student类的对象,并验证新增加的功能。 package xin;import java.util.Scanner;public class Student {    int number;    String classnumber;    int age;    String name;    String sex;       Student(int x,String y,int z,String p){     number = x;     classnumber = y;     age = z;     name = p;    }     int getNumber() {     return number;  //返回学号    }     String getSClassnumber() {     return classnumber;  //返回班号    }     int getAge() {     return age;     //返回年龄    }     String getName() {      return name;    //返回姓名     }     String getSex() {      return sex;   //返回性别     }    public String toString() {      return "学号:"+number+" 班号:"+classnumber+" 姓名:"+name+" 性别:"+sex+" 年龄:"+age;    }   public static void main(String args[]) {   boolean boo,a;   int number1,age1;   String classnumber1,st;   String name1,sex1;   Student stu = new Student(1,"s",1,"xaio");   Scanner reader = new Scanner(System.in);   System.out.println("请输入学号、班号、姓名、性别、年龄:");   stu.number = reader.nextInt();   stu.classnumber = reader.next();   stu.name = reader.next();   stu.sex = reader.next();   stu.age = reader.nextInt();   st = stu.toString();   System.out.println(st);   System.out.println("是否修改该学生信息?");   System.out.println("如果修改则输入true,反之false");    a = reader.nextBoolean();    if(a) {   System.out.println("请输入学号、班号、姓名、性别、年龄:");   number1 = reader.nextInt();   classnumber1 = reader.next();   name1 = reader.next();   sex1 = reader.next();   age1 = reader.nextInt();       System.out.println("学号:"+number1+" 班号:"+classnumber1+     " 姓名:"+name1+" 性别:"+sex1+" 年龄:"+age1);   }   else   {System.out.println("继续执行下一步");}      System.out.println("输入true继续,false退出");        boo = reader.nextBoolean();     System.out.println("*********************");      if(boo) {       main(args);   //函数调用      }      else       System.out.println("程序无法执行!!!!");   }}
  • [交流分享] 计算机为何可以运行Java代码?
    Java代码有很多种不同的运行方式。比如说可以在开发工具中运行,可以双击执行jar文件运行,也可以在命令行中运行,甚至可以在网页。这些执行方式都离不开JRE,Java运行时环境。JRE仅包含运行Java程序的必需组件,包括Java虚拟机以及Java核心类库等。我们Java程序员经常接触到的JDK(Java开发工具包)同样包含了JRE,并且还附带了一系列开发、诊断工具。然而,运行C++代码则无需额外的运行时。往往把这些代码直接编译成CPU所能理解的代码格式,即机器码。比如下图的中间列,就是用C语言写的Helloworld程序的编译结果。C程序编译而成的机器码就是一个个的字节,它们是给机器读的。那为让开发人员也能理解,用反汇编器将其转换成汇编代码(如下图的最右列所示)。; 最左列是偏移;中间列是给机器读的机器码;最右列是给人读的汇编代码0x00:  55                    push   rbp0x01:  48 89 e5              mov    rbp,rsp0x04:  48 83 ec 10           sub    rsp,0x100x08:  48 8d 3d 3b 00 00 00  lea    rdi,[rip+0x3b]                                     ; 加载"Hello, World!\n"0x0f:  c7 45 fc 00 00 00 00  mov    DWORD PTR [rbp-0x4],0x00x16:  b0 00                 mov    al,0x00x18:  e8 0d 00 00 00        call   0x12                                    ; 调用printf方法0x1d:  31 c9                 xor    ecx,ecx0x1f:  89 45 f8              mov    DWORD PTR [rbp-0x8],eax0x22:  89 c8                 mov    eax,ecx0x24:  48 83 c4 10           add    rsp,0x100x28:  5d                    pop    rbp0x29:  c3                    ret为什么Java要在虚拟机里运行?Java作为一门高级程序语言,它的语法非常复杂,抽象程度也很高。因此,直接在硬件上运行这种复杂的程序并不现实。所以呢,在运行Java程序之前,需要对其进行一番转换。转换怎么操作设计一个面向Java语言特性的虚拟机,并通过编译器将Java程序转换成该虚拟机所能识别的指令序列,即Java字节码。之所以这么取名,是因为Java字节码指令的操作码(opcode)被固定为一个字节。下图的中间列,正是用Java写的Helloworld程序编译而成的字节码。可以看到,它与C版本的编译结果一样,都是由一个个字节组成的。同样可以将其反汇编为人类可读的代码格式(如下图的最右列所示)。Java版本的编译结果相对精简一些,Java虚拟机相对于物理机而言,抽象程度更高。# 最左列是偏移;中间列是给虚拟机读的机器码;最右列是给人读的代码0x00:  b2 00 02         getstatic java.lang.System.out0x03:  12 03            ldc "Hello, World!"0x05:  b6 00 04         invokevirtual java.io.PrintStream.println0x08:  b1               returnJava虚拟机常见的是在各个现有平台(如Windows_x64、Linux_aarch64)上提供软件实现。一旦一个程序被转换成Java字节码,便可在不同平台上的虚拟机实现里运行,即“一次编写,到处运行”。虚拟机的另外一个好处是它带来托管环境(Managed Runtime),代替我们处理一些代码中冗长而且容易出错的部分。自动内存管理与垃圾回收,这部分内容甚至催生了一波垃圾回收调优。托管环境还提供了诸如数组越界、动态类型、安全权限等等的动态检测。Java虚拟机具体是怎样运行Java字节码的?以标准JDK中的HotSpot虚拟机为例,从虚拟机以及底层硬件两个角度,给你讲一讲Java虚拟机具体是怎么运行Java字节码的。虚拟机视角,执行Java代码首先要将它编译而成的class文件加载到Java虚拟机。加载后的Java类会被存放于方法区(Method Area)。实际运行时,虚拟机会执行方法区内的代码。这和段式内存管理中的代码段类似。而且,Java虚拟机同样也在内存中划分出堆和栈来存储运行时数据。但Java虚拟机会将栈细分为面向Java方法的Java方法栈,面向本地方法(用C++写的native方法)的本地方法栈,以及存放各个线程执行位置的PC寄存器。运行过程中,每当调用进入一个Java方法,Java虚拟机会在当前线程的Java方法栈中生成一个栈帧,存放局部变量以及字节码的操作数。这个栈帧的大小是提前计算好的,而且Java虚拟机不要求栈帧在内存空间里连续分布。当退出当前执行的方法时,不管是正常返回、异常返回,Java虚拟机均会弹出当前线程的当前栈帧,并舍弃。硬件视角,Java字节码无法直接执行。因此,Java虚拟机需要将字节码翻译成机器码。HotSpot翻译过程有两种形式:解释执行,逐条将字节码翻译成机器码并执行无需等待编译即时编译(Just-In-Time compilation,JIT),将一个方法中包含的所有字节码编译成机器码后再执行实际运行速度更快HotSpot默认采用混合模式,综合了解释执行和即时编译两者的优点:先解释执行字节码,而后将其中反复执行的热点代码,以方法为单位进行即时编译。Java虚拟机的运行效率HotSpot采用了多种技术来提升启动性能以及峰值性能,即时编译便是其中最重要的技术之一。即时编译建立在程序符合二八定律,百分之二十的代码占据了百分之八十的计算资源。对占据大部分的不常用的代码,无需耗费时间将其编译成机器码,而是采取解释执行的方式运行对于仅占据小部分的热点代码,我们则可以将其编译成机器码,以达到理想运行速度。理论即时编译后的Java程序的执行效率,是可能超过C++。因为与静态编译相比,即时编译拥有程序的运行时信息,并且能够根据这个信息做出相应的优化。虚方法是用来实现多态性。对一个虚方法调用,尽管有很多目标方法,但实际运行过程中,可能只调用其中一个。这信息可被即时编译器所利用,规避虚方法调用的开销,达到比静态编译的C++程序更高的性能。为满足不同用户场景的需要,HotSpot内置了多个即时编译器:C1、C2和Graal。Graal是Java 10正式引入的实验性即时编译器。之所以引入多个即时编译器,为在编译时间和生成代码的执行效率之间进行取舍。C1又叫做Client编译器,面向对启动性能有要求的客户端GUI程序,采用的优化手段相对简单,因此编译时间较短。C2又叫做Server编译器,面向对峰值性能有要求的服务器端程序,采用的优化手段相对复杂,因此编译时间较长,但同时生成代码的执行效率较高。从Java 7开始,HotSpot默认采用分层编译的方式:热点方法首先会被C1编译,而后热点方法中的热点会进一步被C2编译。为了不干扰应用的正常运行,HotSpot的即时编译是放在额外的编译线程中进行的。HotSpot会根据CPU的数量设置编译线程的数目,并且按1:2的比例配置给C1及C2编译器。在计算资源充足的情况下,字节码的解释执行和即时编译可同时进行。编译完成后的机器码会在下次调用该方法时启用,以替换原本的解释执行。总结在虚拟机中运行,是因为它提供了可移植性。一旦Java代码被编译为Java字节码,便可以在不同平台上的Java虚拟机实现上运行。此外,虚拟机还提供了一个代码托管的环境,代替我们处理部分冗长而且容易出错的事务,例如内存管理。Java虚拟机将运行时内存区域划分为五个部分,分别为方法区、堆、PC寄存器、Java方法栈和本地方法栈。Java程序编译而成的class文件,需要先加载至方法区中,方能在Java虚拟机中运行。为了提高运行效率,标准JDK中的HotSpot虚拟机采用的是一种混合执行的策略。它会解释执行Java字节码,然后会将其中反复执行的热点代码,以方法为单位进行即时编译,翻译成机器码后直接运行在底层硬件之上。HotSpot装载了多个不同的即时编译器,以便在编译时间和生成代码的执行效率之间做取舍。