当前位置: 首页 > news >正文

什么是成交型网站建设wordpress代码标识

什么是成交型网站建设,wordpress代码标识,国产 做 视频网站,建站 手机网站对象的实例化与直接内存 #x1f604;生命不息#xff0c;写作不止 #x1f525; 继续踏上学习之路#xff0c;学之分享笔记 #x1f44a; 总有一天我也能像各位大佬一样 #x1f31d;分享学习心得#xff0c;欢迎指正#xff0c;大家一起学习成长#xff01; 文章目录…对象的实例化与直接内存 生命不息写作不止 继续踏上学习之路学之分享笔记 总有一天我也能像各位大佬一样 分享学习心得欢迎指正大家一起学习成长 文章目录 对象的实例化与直接内存创建对象的方式① 使用new关键字② 通过反射机制③ 使用克隆的方式④ 反序列化 创建对象的步骤① 判断对象对应的类是否类加载② 为对象分配内存③ 处理并发安全问题④ 初始化⑤ 设置对象的对象头⑥ 执行init方法进行初始化 *对象的布局1). 对象头(Object Header)2). 实例数据(Instance Data)3). 对齐填充(Padding)案例理解 对象的访问定位对象访问的两种方式1). 句柄访问2). 指针访问Hotspot采用此方法 直接内存Direct Memory直接内存概述直接内存的性能直接内存的OOM 总结 在Java中对象实例化是创建一个类的实例的过程。 实例化instantiate是指在面向对象的编程中把用类创建对象的过程称为实例化。是将一个抽象的概念类具体到该类实物的过程。实例化过程中一般由**类名 对象名 new 类名参数1参数2…参数n**构成。 创建对象的方式 在Java中创建对象的方式有许多种简单的概括都有哪些方法。 ① 使用new关键字 使用new 来创建方法是最常见的一种创建方式这种方法会调用构造方法默认是无参构造方法如果有自定义构造方法就会使用自定义构造方法来创建对象。 MyClass myObject new MyClass();还有一种常见的方式就是使用类的静态方法这种方式实际上在静态方法内部也是使用了new的方式来创建这里可以是无参构造也可以是自定义的含参构造器。还有一种也是new方式的变形就是通过一个专门的工厂类或者静态方法来创建对象。 MyClass myObject MyClassFactory.createMyClass();② 通过反射机制 使用 Java 的反射机制可以在运行时获取类的信息并创建对象。但是这种方式只能是使用空参的构造器权限还必须是public。 Class? myClass Class.forName(com.example.MyClass); MyClass myObject (MyClass) myClass.newInstance();在Java9之后就不推荐使用了可以选择使用Constructor的newInstance(xx)。clazz.getDeclaredConstructor().newInstance()这个也是反射的方式可以调用空参、带参的构造器对于权限没有要求。 ③ 使用克隆的方式 通过实现 Cloneable 接口并覆盖 clone 方法可以创建对象的副本。这种方式不调用任何构造器。 MyClass originalObject new MyClass(); MyClass clonedObject (MyClass) originalObject.clone();④ 反序列化 反序列化是将对象从其序列化形式转换回原始对象的过程。在Java中对象可以通过序列化将其状态保存到文件或通过网络传输。反序列化则是从这些序列化的数据中重新构建对象。 除了以上方法还有其他的创建方式比如三方库等。 创建对象的步骤 我们通过字节码来看一下实例化对象的过程。 对于上图中的Object s new Object();首先我们可以将其分成三个部分来看第一个部分Object这个是存放在方法区中的也就是存储着类的信息、常量池、静态变量等等第二部分就是s这个是存在Java栈中这是个引用对象在栈中的本地变量表中存放对象的引用地址指向Java的对象实例数据而第三部分new Object()这是对象的实例数据存在Java堆中。接下来分析一下字节码 // new创建一个对象#2是常量池中对应 java/lang/Object 类的索引 0 new #2 java/lang/Object // dup是复制栈顶元素。在这里复制了刚刚创建的新对象的引用。 3 dup // 调用对象的构造方法。#1 是常量池中对应 init 构造方法的索引V 表示无返回值。这里实际上调用了 java/lang/Object 类的构造方法对新创建的对象进行初始化。 4 invokespecial #1 java/lang/Object.init : ()V // 将栈顶元素即新创建的对象的引用存储到本地变量1中。 7 astore_1 // 返回。这里返回的是 void 类型因为在 Java 中构造方法没有显式的返回值。 8 return以上是从字节码的过程接下来用执行过程来介绍以下分为6个步骤来。 ① 判断对象对应的类是否类加载 首先要实例化一个对象必须加载其对应的类。类加载是Java程序启动的一部分。当程序首次引用一个类时类加载器负责加载这个类的字节码到内存中。这就是加载类。类加载的下一个阶段是链接。在链接过程中将为类的静态变量分配存储空间并且如果存在父类还会链接到父类。链接阶段将符号引用转化为直接引用。在链接中还有验证、准备、解析。 在类的初始化阶段执行类构造器 方法。这是一个特殊的静态方法由编译器生成包含类的静态字段的初始化和静态代码块的执行。初始化是按需进行的即只有在首次实例化类的对象或者首次访问类的静态成员时才会触发。如果一个类有父类那么会首先初始化父类。这个部分在之前的文章《类加载子系统与加载过程》就有描述过这里就是简单复述一下。回归正传当虚拟机遇到一条new指令的时候首先就会先去检查这条指令的参数能否在Metaspace的常量池中定位到一个类的符号引用(就如以上字节码中的0 new #2 java/lang/Object这行字节码#2就是这个Object对象的符号引用)并且会检查符号引用对应的类是否已经被加载、解析和初始化。如果没有将会重新类加载会在双亲委派模式下使用类加载器去查找对应的.class文件。如果没有找到就会抛出ClassNotFoundException异常。 ② 为对象分配内存 一旦类初始化完成JVM 就会为对象分配内存。内存分配的方式可以是在堆上分配也可能是栈上分配具体取决于对象的生命周期和大小。再分配内存首先需要计算对象占用的空间大小接着就是在堆中划分一块内存给新对象如果实例化成员变量是引用变量仅分配引用变量空间即可即4/8个字节大小。 关于字节大小如果操作系统是64位 boolean占1个字节、byte占1个字节 char占2个字节、short占2个字节 int占4个字节、float占4个字节 long占8个字节、double占8个字节 引用变量占8个字节 对于对象内存的分配还需要看内存是否规整。如果内存是规整的那么JVM将采用的是指针碰撞Pointer Bumping[1]来为对象分配内存。 什么是指针碰撞指针碰撞Pointer Bumping是一种内存分配和垃圾回收的实现方式通常用于实现可移植的、线程安全的垃圾回收器。它的工作原理是通过移动指针来分配和回收内存。在使用指针碰撞时整个可用的内存被看作一个大的连续块。实际上意思就是将所有使用过的内存放在一边空闲的内存存在另一边中间放着一个指针作为分界点的指示器分配内存的话就是把指针向空闲内存挪动一段与对象大小相同的距离。如果垃圾收集器是选择Serial、ParNew这种基于压缩算法虚拟机采用这种分配方式一般使用带有compact(整理)过程的收集器是使用指针碰撞。 然而如果内存是不规整的也就是说用过的和没用过的内存相互交错虚拟机就会采用空闲列表法来为对象分配内存也就是虚拟机就需要维护一个列表记录了哪些内存块是可用的再分配的时候会从列表中去找到一块足够大的空间去划分对象实例并更新列表上的内容这种的分配方式就是空闲列表(Free List)。 选择哪种分配方式是由Java堆是否规整决定的而Java堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能来决定。 ③ 处理并发安全问题 在JVM创建对象的过程中是会涉及一些并发安全问题并且也有一些解决策略。 采用CAS失败重试、区域加锁保证原子性 类加载是多线程环境下的一个关键问题。JVM使用类加载锁Class Loading Lock来保证在同一时刻只有一个线程能够加载一个类。这个锁是全局锁确保每个类在同一时刻只能被一个线程加载。 为每个线程都预分配一块TLAB 通过-XX:/-UserTLAB参数设定 ④ 初始化 初始化分配到的空间就是最开始的默认初始化进行默认值设置这能保证对象实例字段在不赋值时就可以直接使用。 属性赋值的操作有几部分首先是属性的默认初始化接着就是显示初始化、代码块中的初始化然后是由构造器进行初始化。 ⑤ 设置对象的对象头 在这一步就会将对象的所属类元数据信息、对象的HashCode和对象的GC信息、锁信息等数据存储到对象的对象头中这个过程的具体设置方式取决于JVM实现。 ⑥ 执行init方法进行初始化 在Java程序的视角来看初始化才是正式开始初始化成员变量执行实例化代码块调用类的构造方法并把堆内对象的首地址赋值给引用变量。这一部分就是将对象属性进行显示初始化、代码块中的初始化、构造器的初始化。 那么怎样才算对象创建完成呢实际上对象的创建从经历了加载类元信息为对象分配内存处理并发问题属性的默认初始化设置对象头的信息数据的显示初始化、代码中初始化以及构造器初始化的几个过程之后才算对象已经创建完成当然如果说对象在默认初始化完毕就算创建完成也是可以的。 *对象的布局 在Hotspot 虚拟机中对象在内存中的存储布局可以分为三个区域对象头(Object Header)、实例数据(Instance Data)、对齐填充(Padding)。在对象头中包含了标记字mark word、类指针(klass word)和 数组长度(array length)。synchronized主要是跟对象头有关系也就是通过mark word的字节位数来表示各种锁状态。 关于锁的知识点之前在《【多线程与高并发】- synchronized锁的认知》就已经介绍过了。 1). 对象头(Object Header) 在HotSpot虚拟机中Java对象的头部包含了一些用于管理对象的元数据信息。其中主要是包含以下两个部分。 Mark Word标记字 占用 8 字节包含了对象的一些状态信息比如哈希值、锁状态标志、线程持有的锁、线程ID、偏向锁时间戳、GC 分代年龄等。Mark Word 的内容在对象的生命周期中可能会发生变化。klass word类型指针Class对象的类型指针 占用 4 字节或 8 字节Jdk1.8默认开启指针压缩后为4字节关闭指针压缩-XX:-UseCompressedOops后长度为8字节1。其指向的位置是对象对应的Class对象其对应的元数据对象的内存地址1。 如果是数组的话还需要记录数组的长度。 2). 实例数据(Instance Data) 这是对象真正存储的有效信息包括程序代码中定义的各种类型的字段也包括了继承父类的和自身的字段。如果存储的数组对象会包含一个用于记录数组长度的字段以及存储了实际数组元素的部分。JVM会根据对象的布局灵活地调整内存布局减小对象头和实例数据的总体大小从而降低内存占用。对于相同宽度的字段会被分配到一起父类定义的变量会出现在子类之前如果CompactFields参数为true也是默认值子类的窄变量可能插入父类的变量空隙。 实例数据主要包括对象的各种成员变量包括基本类型和引用类型。基本类型直接存储内容引用类型则是存储的指针static类型的变量会放到类中而不是放到实例数据里。 3). 对齐填充(Padding) 对齐填充并不是必须的这是一种优化的概念是作为一种占位符的作用。主要是为了满足硬件对齐的要求提高访问效率和性能。通过插入一些额外的字节使得对象的起始地址符合特定的对齐要求。这样可以确保对象的实例数据按照硬件对齐规则排列从而提高内存访问的效率。 案例理解 这里我准备一个案例在Customer类中设置了几个变量并继承父类User其中有一个引用变量Account类。通过Start#main()来实现本次的案例。 // 父类 public class User {String username; } // 子类 public class Customer extends User {int id 1;String name;Account account;{name 默认客户;}public Customer() {account new Account();} } // 引用的类 public class Account {int ids;double money;String tab; } public class Start {public static void main(String[] args) {Customer customer new Customer();/*java对象的内存布局以及使用ClassLayout查看布局*/System.out.println(Customer ClassLayout.parseInstance(customer).toPrintable()); } }这里我引入了ClassLayout来查看Java对象内存布局的需要引入以下坐标。 !-- java对象的内存布局以及使用ClassLayout查看布局 -- dependencygroupIdorg.openjdk.jol/groupIdartifactIdjol-core/artifactIdversion0.17/version /dependency通过运行Start#main()可以清晰看到所占用的字节信息对于Customer对象继承了User类在创建实例的时候会把父类的属性也一并加载过来。首先对象头是固定的8(标记字)4(类型指针)字节实例数据由于会加载父类属性一共是16字节对于引用类型JVM是64位应该是占8字节但是采用了指针压缩所以只占了4字节841628字节不满足对齐要求因此还需要引入对齐填充占4个字节一共就是32字节。 Customercom.lyd.testboot.jvm.objectdemo.Customer object internals: OFF SZ TYPE DESCRIPTION VALUE0 8 (object header: mark) 0x0000000000000001 (non-biasable; age: 0)8 4 (object header: class) 0xf800c18212 4 java.lang.String User.username null16 4 int Customer.id 120 4 java.lang.String Customer.name (object)24 4 com.lyd.testboot.jvm.objectdemo.Account Customer.account (object)28 4 (object alignment gap) Instance size: 32 bytes Space losses: 0 bytes internal 4 bytes external 4 bytes total从以上打印出来的日志能够看到实例中包含了父类与本类的属性以及所占字节数。下图就来介绍整个对象的实例过程。 每个方法就是一个栈帧当我们运行Start#main()因为是main方法在栈帧中的局部变量表中会先加载args里面Customer customer new Customer();会将customer记录到栈帧的局部变量表中通过地址指针指向了堆中的整个实例对象。new Customer()这个实例的内部结构在堆空间记录了对象头、实例数据、填充字节。在该实例的对象头部中记录了标记字主要用与GC、线程安全等通过类型指针指向方法区中对应Customer的klass类元信息。在实例数据中会加载父类的属性以及本身属性Customer类中引用了String对象这是个引用类型通过静态代码块赋值这个字符串存在自负床变量池中。其中也引用了Account对象这也是个引用类型并不会将此对象属性都加载进来只是记录了这个对象的引用地址指向堆空间中的new Account()实例在Account实例中也是与Customer实例一样包含了对象头等信息也是通过类型指针指向方法区Account的klass类元信息。 对象的访问定位 从上文介绍对象的布局就已经能够知道对象是如何获取对象实例的。对于一个Customer customer new Customer();我们知道其存储为三个部分在栈帧中存储了堆区中的引用地址(reference)在堆区有个元数据指针(InstanceOopDesc)指向方法区中的InstanceKlass。总的来说就是栈帧中记录着堆区实例化的对象地址通过这个地址来方法对象实例。 对象访问的两种方式 对象的访问方式主要有两种方式句柄访问和指针访问在hotspot虚拟机中采用的是指针访问的方式。 1). 句柄访问 有些 JVM 实现可能使用了句柄访问的概念其中对象的引用由一个句柄对象来管理而句柄包含了对象的地址以及其他元信息。在Java堆中会开辟一个句柄池与实例池句柄池中会通过指针指向实例对象和对象类型。 因为采用的是句柄池指向对象实例数据在reference中存储的是稳定的句柄地址对象如果被移动垃圾收集时候会移动对象只会改变句柄的实例数据指针就行reference本身是不用做修改。但是需要开辟一个空间来充当句柄池这就会增加堆内存的空间占用。 2). 指针访问Hotspot采用此方法 在栈帧中的本地变量表中记录了堆中对象实例数据的地址通过地址引用到堆中的对象实例数据实例对象在通过类型指针指向方法区中的类元信息。 指针方式不用额外占用堆的空间但是如果遇到对象移动就需要去修改reference存储的地址。 直接内存Direct Memory 直接内存不是JVM运行时数据区的一个部分也不是《Java虚拟机规范》中定义的内存区域它是Java堆外的、直接向系统获取的内存空间。 直接内存概述 在Java虚拟机JVM中“直接内存” 通常指的是使用 NIONew I/O | Non-Blocking I/O包中的 ByteBuffer 类以及其中的 allocateDirect 方法所分配的直接字节缓冲区Direct ByteBuffer。通过DirectByteBuffer操作Native内存。 public class BufferTest {private static final int BUFFER_SIZE 1024 * 1024 * 1024;public static void main(String[] args) {// byteBuffer 将持有一个大小为 1 GB 的直接内存缓冲区ByteBuffer byteBuffer ByteBuffer.allocateDirect(BUFFER_SIZE);System.out.println(内存分配 BUFFER_SIZE byte);Scanner sc new Scanner(System.in);sc.next();System.out.println(释放内存);byteBuffer null;System.gc();} }以上代码我们可以创建出一个内存为1G的直接内存这里通过Scanner输入进行阻塞我们可以通过进程查看所占用的内存大小。可见使用ByteBuffer#allocateDirect()会直接分配本地内存。 直接内存的性能 通常直接内存的速度会比Java堆更快读写性能高。在一些频繁使用IO的场景可能会考虑使用直接内存。Java的NIO包是允许程序使用直接内存用来作为数据缓冲区。对于非直接缓冲区读写文件需要与磁盘交互这时就需要由用户态切换到内核态在由内核态去对物理磁盘进行交互。这样就造成了需要进行两份内存的存储重复数据效率低。 对于直接缓冲区使用NIO就能够直接的使用操作系统给出的缓存区只会存储一份。 直接内存的OOM 直接内存也有可能导致OutOfMemoryError异常。因为直接内存是存在Java堆外的他的大小不会受限于-Xmx设置的最大堆空间系统内存是有限的Java堆和直接内存加起来不能超过操作系统给的最大内存。它是受限于操作系统对进程的可用虚拟内存空间。如果是超过内存的限制就会抛出java.lang.OutOfMemoryError: Direct buffer memory。缺点 分配回收成本比较高不受JVM的内存回收管理 直接内存大小可以通过MaxDirectMemorySize设置如果不指定默认是与堆的最大值-Xmx参数值一致。 总结 本此学习穿插了Java对象的内存布局更加清楚了解到对象的创建方式以及过程最为重要的是了解对象的布局结构包括实例对象数据存放在堆中类元信息在方法区栈帧通过引用去指向对应的数据信息。对比了句柄方式和指针方式。最后学习了直接内存的内容了解了直接内存也是会出现OOM异常。 创作不易如有错误请指正感谢观看记得点赞哦
http://www.zqtcl.cn/news/598999/

相关文章:

  • 图片网站模板wordpress首页模板文件
  • 做外国网站怎么买空间网站策划方案ppt
  • 网站建设全网推广小程序外贸网站建设980
  • 具有营销价值好的网站常德农科院网站
  • 网站域名如何起男女直接做的视频上那个网站
  • 免费创建手机网站上海网站设计建设
  • 校园网站建设招标公告网站开发常用问题
  • 信息公开和网站建设工作总结开网站建设公司赚钱吗
  • 恋月wordpress主题优化大师兑换码
  • 河南省住房和城乡建设厅网站查证网页设计大赛海报
  • 莱芜金点子信息港厂房出租国内正规seo网络推广
  • 番号网 wordpressseo搜索排名影响因素主要有
  • 网站后台开发语言中山市网站建设
  • 可以免费下源码的网站石家庄市里的网站公司
  • wordpress的别名获得页面的别名优化大师电视版
  • 怎么查网站关键词排名微信上的h5页面是怎么制作的
  • 如何为一个网站做app手机软件大全
  • 哪家网络公司做网站工信部网站原来是
  • json取数据做网站asp网站 模板
  • 漳州做网站多少钱乐清网红餐厅
  • 淮安网站开发sem推广案例
  • 义乌网站建设郭云砺信息科技做网站
  • 重庆御临建筑公司官网网站更换域名seo
  • 北京大兴专业网站建设公司wordpress 加速乐
  • win7怎么做网站域名绑定邯郸最新通知今天
  • 苏州企业网站设计开发个人 网站备案
  • 威海哪有网站建设中国建设部网站失信名单
  • 重庆哪家在做网站建设php网站后台验证码不显示
  • 开发网站开票写什么google收录查询
  • dw做的网站如何上传图片服务器配置wordpress