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

wordpress电子商务网站插件用ps怎么做网站效果图

wordpress电子商务网站插件,用ps怎么做网站效果图,做一个网站设计要多少钱,重庆小程序制作引言 我在上篇文章#xff1a;JVM | 基于类加载的一次完全实践 中为你讲解如何请“建筑工人”来做一些定制化的工作。但是#xff0c;大型的Java应用程序时#xff0c;材料#xff08;类#xff09;何止数万#xff0c;我们直接堆放在工地上#xff08;JVM#xff09;…引言 我在上篇文章JVM | 基于类加载的一次完全实践 中为你讲解如何请“建筑工人”来做一些定制化的工作。但是大型的Java应用程序时材料类何止数万我们直接堆放在工地上JVM上吗相反JVM有着一套精密的管理机制来确保类的加载、验证、解析和初始化等任务能够有序且高效地完成。 在Java的世界中虚拟机JVM是我们每一个程序的运行环境而它的内存结构更是决定我们程序运行性能的关键因素。理解JVM的内存结构不仅可以帮助我们编写出更高效的代码而且可以在程序出现问题时更快地定位并解决问题。然而JVM内存结构的复杂性很多人仍然存在许多误解和疑惑。 在本篇文章中我们将详细地探讨这些“建筑工人”是如何处理“建筑材料”的从而帮助你更深入地理解JVM类加载和初始化的内部工作机制。希望通过这篇文章可以带你更深入地理解Java程序的运行机制。让我们开始吧 类的加载 我在之前为你讲解了类的生命周期你还记得吗我们来回顾下加载、验证、准备、解析、初始化、使用和卸载。 接下来我们再深入分析完整的过程。 加载类进JVM内存 还是以Building为例。假设你在编译器中编写了Building类并生成了相应的字节码文件Building.class。当你启动你的Java程序时首先JVM启动并初始化。在这个过程中JVM的类装载子系统起着关键的作用。类装载子系统的主要职责就是加载类到JVM中。当类被加载时Java虚拟机首先将类的元信息放入运行时数据区的元空间中然后在堆中生成java.lang.Class类的实例。这个Class对象会包含指向元空间中类元信息的引用。文字还是过于抽象我画了一张图你看 这里有几个让人混淆的地方我来为你解释一下 两个Class 图中有两处Building.class。但是此Class非彼Class。第一步的Class代表着Building的字节码文件。而第二步的Class则为指向Building类元信息的Class对象。 两处元空间 这里我从不同的JDK内存结构讲起你可以比较这两者差异 在JDK7里类元数据信息被存储在堆的一部分叫做方法区它需要参与垃圾回收但时常被GC忽略。所以方法区的存在让内存管理成本变高而且在空间分配不当的情况下容易出现内存溢出的情况。 所以在JDK8时将方法区改为元空间并把其移到本地内存中这样可以更好地管理内存避免出现内存溢出的情况。 JVM内存和直接内存 在图中你可以看到JVM内存和本地内存都属于(物理)内存的一部分为什么要把它们分开讨论呢因为目标不同JVM是由JVM进程管理的一块内存空间它可以对其中的内存进行自动垃圾收集。而本地内存是不受JVM管理而且不受JVM内存设置的限制。 直接内存和操作系统内存 虽然直接内存不受垃圾回收管理。但是它依然是Java虚拟机从操作系统申请的。它可以用于高效的I/O操作如果你想使用直接内存空间可以使用这个方法ByteBuffer.allocateDirect() 。 类的链接过程 接下来我们看下链接的过程链接分为三步验证阶段准备阶段解析阶段。这个过程由类加载子系统来完成我们来看下 验证阶段 JVM 读取类文件后需要对其进行验证确保这个类文件满足 JVM规范要求不会有安全问题。 准备阶段 JVM 为类的静态变量分配内存并且为它们设置默认值。在我们的 Building 类中constructionYear 就是一个静态变量所以它会在这个阶段被初始化为 0对于 int 类型初始化默认值为 0。静态变量是属于类的我们会把它放在元空间中你看 解析阶段 JVM 将类的二进制数据中的符号引用替换为直接引用。这个过程是在元空间完成的。符号引用就是一组符号来描述所引用的目标直接引用就是直接指向目标的指针、相对偏移量或者是一个能直接定位到目标的句柄。 直接引用好理解符号应用是啥以Building为例符号引用就是org.kfaino.jvm.Building.construct:()Lorg/kfaino/jvm/Building; 这两个东西都在元空间的运行时常量池中你看 类的初始化阶段 在讲类初始化之前我们应该要知道类什么时候开始初始化什么时候又不初始化这里也是面试的常考题我们来重点分析下。 类什么时候不初始化 我直接以代码举例你可以看下 static String CONSTANT 我是静态常量我要被放到堆的常量池里面了; static int i 128; 这里展示了两种情况引用类型的String会被放到堆的字符串常量池中而int类型则会被放在上面的元空间的静态变量中你可以结合上面的图理解。接下来我们看下初始化的情况。 类什么时候开始初始化 还是以代码举例你可以看下 Building building new Building(); Building.静态方法(); // 如果initializeBoolean为false也不会初始化 Class? clazz Class.forName(org.kfaino.jvm.Building);// 作为父类的情况 class SubBuilding extends Building {}看完这些初始化的情况之后我们来看下具体是怎么初始化的。 类的初始化 初始化阶段首先会为对象分配内存内存分配完成后需要将分配给对象的内存空间都初始化为零值分配零值。然后设置对象头。分配内存好理解因为当Class被加载进元空间中就已经可以算出每个类型的内存大小了。至于对象头我打算在垃圾回收时为你讲解限于篇幅这里按下不表。 这里的分配零值也有可考的内容你看 public class ZeroTest {int i; public void testMethod() {int j; System.out.println(i); // Variable j might not have been initializedSystem.out.println(j); } }因为i在初始化时有分配0所有可以正常输出。但是j是局部变量没有初始化就会报错。 做完这三件事之后JVM 会执行类的初始化代码。对于 Building 类来说constructionYear 在这个阶段会被初始化为 2023这个值是在类的静态初始化器clinit中设置的。 我在上篇文章中说到如果我们在多线程中使用类加载器可能会导致类被重复加载多次。除了会浪费资源外还会导致我们一些静态初始化代码被执行多次。 指的就是clinit 。有关也有一个常见的面试题我为你展示代码你暂停思考下结果如何 public class Building {static int constructionYear 2023;static {constructionYear 2024;}public static void main(String[] args) {System.out.println(constructionYear);} }想好了吗最终答案是2024。因为静态变量和静态代码块会放在静态初始化器中按顺序执行的。 使用 在完成初始化后类就可以被应用程序正常使用了。当你调用一个方法时JVM会为这个方法创建一个新的栈帧并压入到当前线程的Java栈中。Java栈是线程私有的内存区域用于存储每个方法调用的状态包括局部变量、操作数栈、动态链接等信息。 方法调用 方法调用具体过程是什么样的呢 依然以 Building 为例 我i先改造下它加上一个计算建筑年龄的方法你看 public class Building {private static final int CONSTRUCTION_YEAR 1998;public int calculateAge(int currentYear) {return currentYear - CONSTRUCTION_YEAR;} }接下来假设有一段代码调用了 calculateAge 方法 public static void main(String[] args) {Building building new Building();int age building.calculateAge(2023); }当 calculateAge 方法被调用时我们来看下在JVM虚拟机内存发生了什么为了方便你理解 我事先画了一张图你看 我在图中完整标注出执行顺序你可以暂停看下。接下来我详细的为你解释 方法调用当Java代码执行到building.calculateAge(2023)时首先JVM会通过对象引用即building查找到类Building然后在类中查找calculateAge方法的符号引用。动态链接JVM会根据Building类中的符号引用找到calculateAge方法在运行时常量池中的直接引用获取改方法的内存地址。创建新的栈帧JVM为调用的方法创建一个新的栈帧并推入当前线程的Java栈顶。这个栈帧包含局部变量表、操作数栈、动态链接和方法出口。初始化局部变量表JVM将方法调用的参数即currentYear和this存储到新栈帧的局部变量表中。更新程序计数器JVM的程序计数器更新为calculateAge方法的第一条字节码指令。执行方法体 JVM开始执行calculateAge方法的字节码。当执行到currentYear - CONSTRUCTION_YEAR时它会将currentYear和CONSTRUCTION_YEAR推入操作数栈然后执行减法操作并将结果推入操作数栈顶。方法返回执行完calculateAge方法后JVM将操作数栈顶的结果即年龄作为方法返回值并将calculateAge方法的栈帧从Java栈中弹出。接收返回值calculateAge方法的返回值被推入调用者即main方法的操作数栈中并赋值给局部变量age。更新程序计数器JVM的程序计数器更新为main方法的下一条指令。 至此我们就完成了从类的加载到类的实例化再到类的使用完整的过程。在这个过程中你可以看到JVM运行时数据区的各个部分是如何协同工作的。细心体会之后你会发现类的加载和初始化阶段主要与元空间有关而类的实例化阶段主要与堆有关。顺便我画了一张图你可以看一下 接下来我们来看下类不用之后如何被卸载。 卸载 垃圾回收 当Building对象不再被任何引用变量引用时对象不可达它就成为了垃圾。在某个时间点垃圾收集器会回收这个对象占用的堆内存这块我将在后续的垃圾回收为你详细讲解。 类的完全卸载 如果Building类的ClassLoader实例被回收且没有任何线程在Building类的方法内执行且没有任何Java栈帧持有Building类的方法的引用那么JVM会判断Building类可以被卸载并可能在未来的某个时间点由垃圾收集器回收其在元空间内占用的内存。对你没听错。方法区也可以进行垃圾回收。但是类的完全卸载是一件苛刻的事情你还记得我在第一篇文章中说的AppClassLoader吗它是由BootstrapClassLoader创建它的生命周期与JVM一样长不会被垃圾回收。所以由AppClassLoader创建的类不会被卸载。当然如果你想要卸载类可以用第二篇文章中的自定义类加载器。 文中重要部分解析 初始化和未初始化 我在前面强调什么时候会进行类的初始化阶段什么会只进行加载和链接。知道这两个差异有什么用呢我们在编写代码的时候可以减少内存开销我们现在知道类的初始化阶段需要分配内存如果我们写一个懒加载在使用时才初始化那么我们的内存就会减少很多。相信你已经明白它的价值了。当然空有概念没有代码可不行我为你举一个例子你可以看下 public class ConfigManager {private MapString, SupplierConfig allConfigs new HashMap();public ConfigManager() {// 在初始化阶段只是将配置类的构造函数注册到map中allConfigs.put(config1, Config1::new);allConfigs.put(config2, Config2::new);// ...allConfigs.put(configN, ConfigN::new);}public Config getConfig(String name) {return allConfigs.get(name).get();} }相比原来new的操作我使用了Config1::new。它不会在一开始就被初始化而是在我们getConfig()的时候才进行初始化。这就是专家级和普通级别程序员的差距。 直接内存VSJVM内存 我在之前为你提到ByteBuffer.allocateDirect() 方法它可以使用直接内存。用直接内存有什么好处答案是可以减少内存复制的开销直接缓冲区可以直接在内存中进行数据操作无需将数据复制到Java堆内存中。还是老规矩我用代码为你演示一个读取文件IO的场景你看 // 一个5G的视频private static final String FILE_PATH C:\\Users\\xxx\\Desktop\\1.mp4;// 1MBprivate static final int BUFFER_SIZE 1024 * 1024;public static void main(String[] args) throws Exception {// 我用了懒加载testBufferAllocator(ByteBuffer::allocate, Heap Buffer);testBufferAllocator(ByteBuffer::allocateDirect, Direct Buffer);}private static void testBufferAllocator(BufferAllocator allocator, String testName) throws Exception {try (FileChannel channel FileChannel.open(Paths.get(FILE_PATH), StandardOpenOption.READ)) {ByteBuffer buffer allocator.allocate(BUFFER_SIZE);Instant start Instant.now();while (channel.read(buffer) 0) {buffer.clear();}Instant end Instant.now();System.out.printf(%s: %s ms%n, testName, Duration.between(start, end).getNano() / 1000000);}}private interface BufferAllocator {ByteBuffer allocate(int capacity);}我分别用堆缓存和直接缓存来测试它们两个的吞吐量。我们来看下结果 Connected to the target VM, address: 127.0.0.1:5061, transport: socket Heap Buffer: 934 ms Direct Buffer: 765 ms Disconnected from the target VM, address: 127.0.0.1:5061, transport: socketProcess finished with exit code 0直接内存比堆内存快了将近200ms。这两种内存的差距就在于堆内存多出了数据从内核缓冲区复制到Java堆内存中的缓冲区步骤。 关于intern()方法 我在上面说到String类型的静态变量会被放到堆的字符串常量池中。它的目的就是为了减少相同字符串初始化带来的开销。当然这样的设计就会带来一个问题。你来看下这段代码 String s1 Building; String s2 new String(Building); System.out.println(s1 s2); System.out.println(s1 s2.intern()); 输出结果是多少呢暂停思考下有答案了你再接着往下看 我来公布答案第一个为false 因为 s2 是一个新的字符串实例第二个为true因为 s2.intern() 返回的是字符串常量池中的 “Hello” 如果你感兴趣还可以阅读官方文档我对相关部分进行了截图你可以看下链接已放在参考文献中如果你感兴趣也可以阅读。 总结 至此本篇完结。我们来回顾一下本篇文章是类加载过渡到JVM内存结构的衔接文章。为了让你把之前的知识串起来我结合了内存结构重新为你讲解类的生命周期。希望看完这篇文章你会有不一样的收获。 参考文献 Java虚拟机规范Java SE 8版JVMInternalsJavaGuide Java内存区域详解 后续 本篇文章从类的完整生命周期的角度为你深入解析了JVM内存结构但仍有一些细节未涉及例如本地方法栈的具体工作方式以及本地方法是C代码它是如何运作的在接下来的文章中我将进一步展开为你勾勒出JVM内存结构的全貌让你对其有更深入、全面的理解。敬请继续关注
http://www.hkea.cn/news/14342822/

相关文章:

  • 网站策划哪里找怎么选择赣州网站建设
  • 纺织面料做哪个网站好wordpress wp-polls
  • 福建省建设厅网站 企业高德地图怎么没有菲律宾位置
  • 福州网站建设哪家好您与此网站建立的连接不安全
  • 惠州城乡住房建设厅网站移动4G网站建设
  • 养殖网站 模板在线商城app下载
  • 安徽建站公司网络工程师工资一般多少的
  • 如何建设自己网站wordpress标签生成图片
  • 网站 多少篇文章才能上线公司官网查询
  • 域名转出过程网站能打开吗江苏建设机械网站
  • 建设网站实施条件网站建设开发简介
  • 怎做卖东西的网站wordpress mysqli
  • 网站右侧浮动广告学习网站建设优化
  • 完整的网站开发流程英文网站设计哪里好
  • 东莞品托网站建设网络营销实务
  • 百度推广建站平台wordpress模版如何使用
  • 绿色建筑网站wordpress to phonegap
  • 做网站的的广告词泉州市城乡和住房建设网站
  • 网站开发哪里好淘宝客网站素材
  • 免费发布信息网站建站平台代理
  • 做网站主机电脑千锋教育培训多少钱
  • vue2.0网站开发h5游戏源码网
  • php 网站开发收费第三方平台做网站网站
  • 建设联结网同类网站做网站需要会什么条件
  • 大型购物网站建设方案新华网
  • 小程序制作二维码南昌seo网络
  • ps6做网站点哪里保存网站只有一个首页单页面怎么做排名
  • 网站进行中英文转换怎么做sql数据库的网站迁移
  • 深圳cms建站系统耐克网站建设的历程
  • html 网站链接网站是用什么语言写的