文章目录
联系我上次写的关于Java内存的文章,对象访问在 Java 语言中无处不在,是最普通的程序行为,但即使是最简单的访问,也会却涉及 Java 栈、Java 堆、方法区这三个最重要内存区域之间的关联关系,如下面的这句代码:
Object obj = new Object();
1、假设这句代码出现在方法体中,那"Object obj"这部分的语义将会反映到Java 栈的本地变量表中,作为一个 reference 类型数据出现。
2、而"new Object()"这部分的语义将会反映到Java 堆中,形成一块存储了 Object 类型所有实例数据值(Instance Data,对象中各个实例字段的数据)的结构化内存,根据具体类型以及虚拟机实现的对象内存布局(Object Memory Layout)的不同,这块内存的长度是不固定的。
3、另外,在 Java 堆中还必须包含能查找到此对象类型数据(如对象类型、父类、实现的接口、方法等)的地址信息,这些类型数据则存储在方法区中。
那在new对象的过程中,究竟发生了什么?JVM做了哪些工作?
实质上,Java在new一个对象的时候,会先查看对象所属的类有没有被加载到内存,如果没有的话,就会先通过类的全限定名(包名+类名)来加载。加载并初始化类完成后,再进行对象的创建工作。
我们下面的讨论是假设是第一次使用该类,这样的话new一个对象就可以分为两个过程:加载并初始化类和创建对象。
类加载概述
虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最中形成可以被虚拟机直接使用的Java类型,这就是虚拟机的 类加载机制。
与那些在编译时需要注意连接工作的语言不同,在Java语言里面,类型的加载和连接过程都是在程序运行期间完成的,这样会在类加载时稍微增加一些性能开销,但是却能为语言本身提供高度的灵活性,Java中天生可以动态扩展的语言特性就是依赖于运行期动态加载和动态连接这个特点实现的。
类的生命周期:
Java是使用双亲委派模型来进行类的加载的,所以在描述类加载过程前,我们先看一下 双亲委托模型的工作过程:
如果一个类加载器(ClassLoader)收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委托给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需要加载的类)时,子加载器才会尝试自己去加载。
类加载器双亲委派模型:
1、启动类加载器:加载系统环境变量下JAVA_HOME/lib目录下的类库;
2、扩展类加载器:加载JAVA_HOME/lib/ext目录下的类库;
3、应用程序类加载器(系统类加载器):加载用户类路径Class_Path指定的类库。(我们可以在使用第三方插件时,把jar包添加到ClassPath后就是使用了这个加载器);
4、自定义加载器:如果需要自定义加载时的规则(比如:指定类的字节流来源、动态加载时性能优化等),可以自己实现类加载器;
使用双亲委托机制的好处是:
能够有效确保一个类的全局唯一性,当程序中出现多个限定名相同的类时,类加载器在执行加载时,始终只会加载其中的某一个类。
加载
什么情况下需要开始类加载过程的第一个阶段:加载。
【了解】:
虚拟机规范并没有进行强制约束,这点可以交给虚拟机的具体实现来自由把握。但是对于初始化阶段,虚拟机规范则是有严格规定了有且只有4种情况必须立即对类进行“初始化”(而加载、验证、准备自然要在此之前开始):1、遇到new 、getstatic、putstatic、invokestatic这4条字节码指令时,如果类没有初始化,则需要先触发其初始化;2、使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行初始化,则需要先触发其初始化;3、当初始化一个类的时候,如果父类没有初始化,需要先初始化父类;4、当虚拟机启动时,用户需要指定一个主类(包含main()那个类),虚拟机会先初始化这个类。
加载与类加载的区别:
“加载”(Loading)阶段是“类加载(Class Loading)”过程的一个阶段,希望不要混淆。
加载阶段需要做的3件事情:
通过一个类的全限定名来获取定义此类的二进制字节流;
将这个字节流所代表的静态存储结构转换为方法区的运行时数据结构;
Java代码在进行javac编译的时候,并不像C/C++那样有“连接”这一步骤,而是在虚拟机加载Class文件的时候进行动态连接。也就是说,在Class文件中不会保存各个方法和字段的最终布局信息,因为这些字段和方法的符号引用不经过转换的话是无法直接被虚拟机使用的。当虚拟机运行的时候,需要从常量池获得对应的符号引用,再在类创建时或运行时解析并翻译到具体的内存地址中。
在Java堆中生成一个代表这个类的java.lang.class对象,作为方法区这些数据的访问入口;
验证
验证时连接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
为什么要进行验证?
【了解】:
Java语言本身是相对安全的语言(相对C/C++),使用纯粹的Java代码无法做到诸如访问数组边界以外的数据、将一个对象转型为他并未实现的类型、跳转到不存在的代码行之类的事情,如果这样做了,编译器将拒绝编译。但是Class文件并不一定要求使用Java源码编译而来,可以使用任何途径。在字节码的语义层面上,上述Java代码无法做到的事都是可以实现的,虚拟机如果不检查输入的字节流,对其完全信任的话,很可能会因为载入了有毒的字节流而导致系统崩溃,所以验证是虚拟机对自身保护是十分重要的一项工作。
如何进行验证?
【了解】:
准备
准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配。这个阶段有两个容易混淆的概念:
这个是时候进行内存分配的仅包括类变量(被static修饰的变量),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在Java堆中。
解析
解析阶段是虚拟机将常量池中的符号引用替换为直接引用的过程;
符号引用:用一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义的定位到目标即可;
直接引用:可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。
直接引用与虚拟机实现的内存布局相关的,同一个符号引用在不同虚拟机实例上翻译出来的直接引用一般不会相同。如果有了直接引用,那引用的目标已经存在于内存中。
解析动作主要针对于类或接口、字段、类方法、接口方法四类符号引用进行;
以上2、3、4三个阶段又合称为链接阶段,链接阶段要做的是将加载到JVM中的二进制字节流的类数据信息合并到JVM的运行时状态中。
初始化
是所有类加载过程的最后一步,前面的过程除了加载阶段用户应用程序可以通过自定义类加载器参与之外,其余动作完全由虚拟机主导和控制。到了初始化阶段,才真正开始执行类中定义的Java程序代码(或者说字节码);
在准备阶段,变量已经赋过一次系统要求的初始值,而在初始化阶段,则是根据程序员通过程序制定的主观计划去初始化类变量和其他资源;
主要分为:
1、为静态变量赋值;
2、执行static代码块(注意:static代码块只有JVM能够调用);
如果是多线程需要同时初始化一个类,仅仅只能允许其中一个线程对其执行初始化操作,其余线程必须等待,只有在活动线程执行完对类的初始化操作之后,才会通知正在等待的其他线程。
因为子类存在对父类的依赖,所以类的加载顺序是先加载父类后加载子类,初始化也一样。不过, 父类初始化时,子类静态变量的值也有有的,是默认值。
最终,方法区会存储当前类类信息,包括类的静态变量、类初始化代码(定义静态变量时的赋值语句和静态初始化代码块)、实例变量定义、实例初始化代码(定义实例变量时的赋值语句实例代码块 和构造方法)和实例方法,还有父类的类信息引用。
在堆区分配对象需要的内存
分配的内存包括本类和父类的所有实例变量,但不包括任何静态变量;
对所有实例变量赋默认值
将方法区内对实例变量的定义拷贝一份到堆区,然后赋默认值 ;
执行实例初始化代码
初始化顺序是先初始化父类再初始化子类,初始化时先执行实例代码块然后是构造方法;(代码块先于构造方法执行)
在栈区开辟空间
如果有类似于Object obj = new Object()形式的obj引用的话,在栈区定义Object 类型引用变量obj,然后将堆区对象的地址赋值给它;
通过实例引用调用实例方法的时候,先从方法区中对象的实际类型信息找,找不到的话再去父类类型信息中找。
如果继承的层次比较深,要调用的方法位于比较上层的父类,则调用的效率是比较低的,因为每次调用都要经过很多次查找。这时候大多系统会采用一种称为虚方法表的方法来优化调用的效率。
所谓虚方法表,就是在类加载的时候,为每个类创建一个表,这个表包括该类的对象所有动态绑定的方法及其地址,包括父类的方法,但一个方法只有一条记录,子类重写了父类方法后只会保留子类的。当通过对象动态绑定方法的时候,只需要查找这个表就可以了,而不需要挨个查找每个父类。
原文链接:https://blog.csdn.net/weixin_45082647/java/article/details/105520394
本文由哈喽比特于4年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/7_-Pwgipyr9F2657WNucDA
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。