首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

java-jvm

JVM中类的装载是由ClassLoader和它的子类来实现的,Java ClassLoader 是一个重要的Java运行时系统组件。它负责在运行时查找和装入类文件的类。 Java中的所有类,都需要由类加载器装载到JVM中才能运行。类加载器本身也是一个类,而它的工作就是把class文件从硬盘读取到内存中。在写程序的时候,我们几乎不需要关心类的加载,因为这些都是隐式装载的,除非我们有特殊的用法,像是反射,就需要显式的加载所需要的类。 类装载方式,有两种 (1)隐式装载,程序在运行过程中当碰到通过new 等方式生成对象时,隐式调用类装载器加载对应的类到jvm中,利用反射即隐式加载可绕过一些权限检查机制。 (2)显式装载,通过class.forname()等方法,显式加载需要的类 ,隐式加载与显式加载的区别:两者本质是一样的。 java中类加载是动态的,并不会一次性把所有的类全部加载后再运行,而是保证程序运行的基础类(像是基类)完全加载到jvm中,至于其他类,则在需要的时候才加载。这当然就是为了节省内存开销。

04
  • 您找到你想要的搜索结果了吗?
    是的
    没有找到

    泛型和元编程的模型:Java, Go, Rust, Swift, D等

    在程序设计的时候,我们通常希望使用同样的数据结构或算法,就可以处理许多不同类型的元素,比如通用的List或只需要实现compare函数的排序算法。对于这个问题,不同的编程语言已经提出了各种各样的解决方案:从只是提供对特定目标有用的通用函数(如C,Go),到功能强大的图灵完备的通用系统(如Rust,C++)。在本文中,我将带你领略不同语言中的泛型系统以及它们是如何实现的。我将从C这样的不具备泛型系统的语言如何解决这个问题开始,然后分别展示其他语言如何在不同的方向上逐渐添加扩展,从而发展出各具特色的泛型系统。 泛型是元编程领域内通用问题的简单案例:编写可以生成其他程序的程序。我将描述三种不同的完全通用的元编程方法,看看它们是如何在泛型系统空的不同方向进行扩展:像Python这样的动态语言,像Template Haskell这样的过程宏系统,以及像Zig和Terra这样的阶段性编译。

    03

    Synchronized的实现原理

    Synchronized是由JVM实现的一种实现互斥同步的一种方式,如果你查看被Synchronized修饰过的程序块编译后的字节码,会发现,被Synchronized修饰过的程序块,在编译前后被编译器生成了monitorenter、monitorexit两个字节码指令。 这两个指令是什么意思呢?    在虚拟机执行到monitorenter指令时,首先要尝试获取对象的锁:如果这个对象没有锁定,或者当前线程已经拥有了这个对象的锁,把锁的计数器+1;当执行monitorexit指令时将锁计数器-1;当计数器为0时,锁就被释放了。如果获取对象失败了,那当前线程就要阻塞等待,直到对象锁被另外一个线程释放为止。Java中Synchronize通过在对象头设置标记,达到了获取锁和释放锁的目的。

    05

    笔记:追随云原生的Java

    但在微服务时代是提倡服务围绕业务能力(不同的语言适合不同的业务场景)而非技术来构建应用,不再追求实现上的一致,一个系统由不同语言、不同技术框架所实现的服务来组成是完全合理的。服务化拆分后,很可能单个微服务不再需要再面对数十、数百 GB 乃至 TB 的内存。有了高可用的服务集群,也无须追求单个服务要 7×24 小时不可间断地运行,它们随时可以中断和更新。不仅如此,微服务对镜像体积、内存消耗、启动速度,以及达到最高性能的时间等方面提出了新的要求。这两年的网红概念 Serverless(以及衍生出来的Faas) 也进一步增加这些因素的考虑权重,而这些却正好都是 Java 的弱项:哪怕再小的 Java 程序也要带着厚重的Rumtime(Vm和StandLibrary)——基于 Java 虚拟机的执行机制,使得任何 Java 的程序都会有固定的内存开销与启动时间,而且 Java 生态中广泛采用的依赖注入进一步将启动时间拉长,使得容器的冷启动时间很难缩短。 举两个例子。软件工业中已经出现过不止一起因 Java 这些弱点而导致失败的案例。如 JRuby 编写的 Logstash,原本是同时承担部署在节点上的收集端(Shipper)和专门转换处理的服务端(Master)的职责,后来因为资源占用的原因,被 Elstaic.co 用 Golang 的 Filebeat 代替了 Shipper 部分的职能。又如 Scala 语言编写的边车代理 Linkerd,作为服务网格概念的提出者,却最终被 Envoy 所取代,其主要弱点之一也是由于 Java 虚拟机的资源消耗所带来的劣势。 1.变革之火 1.1 Complie Native Code 显然,如果将字节码直接编译成可以脱离 Java 虚拟机的原生代码则可以解决所有问题。 如果真的能够生成脱离 Java 虚拟机运行的原生程序,将意味着启动时间长的问题能够彻底解决,因为此时已经不存在初始化虚拟机和类加载的过程。也意味着程序马上就能达到最佳的性能,因为此时已经不存在即时编译器运行时编译,所有代码都是在编译期编译和优化好的。同理,厚重的Runtime也不会出现在镜像中。 Java 并非没有尝试走过这条路。从GCJ到 Excelsior JET再到 GraalVM 中的 SubstrateVM 模块再到 2020 年中期建立的 Leyden 项目,都在朝着提前编译(Ahead-of-Time Compilation,AOT)生成原生程序这个目标迈进。Java 支持提前编译最大的困难在于它是一门动态链接的语言,它假设程序的代码空间是开放的(Open World),允许在程序的任何时候通过类加载器去加载新的类,作为程序的一部分运行。要进行提前编译,就必须放弃这部分动态性,假设程序的代码空间是封闭的(Closed World),所有要运行的代码都必须在编译期全部可知。 这一点不仅仅影响到了类加载器的正常运作,除了无法再动态加载外,反射(通过反射可以调用在编译期不可知的方法)、动态代理、字节码生成库(如 CGLib)等一切会运行时产生新代码的功能都不再可用——如果将这些基础能力直接抽离掉,Hello world 还是能跑起来,大部分的生产力工具都跑不起来,整个 Java 生态中绝大多数上层建筑都会轰然崩塌。随便列两个Case:Flink的SQL API会解析SQL并生成执行计划,这个时候会通过JavaCC动态生成类加载到代码空间中去;Spring也有类似的情况,当AOP通过动态代理的方式去生成相关逻辑时,本质还是在Runtime时生成代码并加载进去。 要获得有实用价值的提前编译能力,只有依靠提前编译器、组件类库和开发者三方一起协同才可能办到——可以参考Quarkus。 Quarkus和我们上述的方法如出一辙,以Dependency Inject为例:所有要运行的代码都必须在编译期全部可知,在编译期就推导出来相关的Bean,最后交给 GraalVM来运行。 1.2 Memory Access Efficiency Improvement Java 即时编译器的优化效果拔群,但是由于 Java“一切皆为对象”的前提假设,导致它在处理一系列不同类型的小对象时,内存访问性能很差。这点是 Java 在游戏、图形处理等领域一直难有建树的重要制约因素,也是 Java 建立 Valhalla 项目的目标初衷。 这里举个例子来说明此问题,如果我想描述空间里面若干条线段的集合,在 Java 中定义的代码会是这样的: public record Point(float x, float y, float z) {} public record Line(Point start, Point end) {} Line[] lines; 面向对象的内存布局中,对象标识符(Object Ident

    01
    领券