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

11 java语言执行浅析1

Java 是基于线程模型的语言,程序的执行是通过线程来调度和运行的。


一、Java 的最小执行单元:线程(Thread)

  • 每个 Java 程序至少有一个主线程(main thread),它从 main() 方法开始执行。
  • 所有代码最终都在线程中运行,包括方法调用、对象创建等操作。
  • 可以通过继承 Thread 类或实现 Runnable 接口创建多线程程序。
public class MyThread extends Thread {public void run() {System.out.println("Thread is running");}public static void main(String[] args) {MyThread t = new MyThread();t.start(); // 启动线程}
}

 结论:线程才是 Java 的最小执行单元;方法只是逻辑组织单位,并不能独立执行。


Java 与 Go 的面向对象/结构化设计差异 

Java:一切必须封装在类中(OOP)

  • Java 是一门典型的 面向对象编程语言(OOP)
  • 所有代码都必须写在类(class)内部,包括方法、变量、静态块等。
  • 没有“函数”这个一级类型,只有类中的方法。即使是一个简单的 main() 方法也必须定义在一个类里。
特点:
  • 强调封装性、模块性和可扩展性;
  • 更适合大型企业级应用开发;
  • 支持继承、多态、接口等 OOP 高级特性。
  • (X)灵活性略差,语法繁琐;
  • (X)对于简单脚本或小型项目不够轻便。

 Go:函数是一等公民,支持自由函数(FP + OOP)

  • Go 是一种 过程式 + 接口 + 并发模型 的语言,强调简洁和高效。
  • 函数是“一等公民”,可以直接定义并作为参数传递。
  • Go 中的函数可以独立存在,不依赖类结构。
  • 类型系统简单,没有继承,而是通过组合实现复用。
特点:
  • 更加灵活,适合并发、网络服务等场景;
  • 语法简洁,学习成本低;
  • 内置 goroutine 和 channel 支持 CSP 并发模型。
  • (X)缺乏传统 OOP 的继承机制;
  • (X)不适合构建复杂的类层次结构。

我们可以从 Java 的 动态代理(Dynamic Proxy) 和 Go 的 高阶函数(Higher-order Function) 来对比说明。

Java 动态代理基于 JVM 提供的 Proxy 类和 InvocationHandler 接口

public interface Service {void doSomething();
}public class RealService implements Service {public void doSomething() {System.out.println("Doing something...");}
}public class LoggingInvocationHandler implements InvocationHandler {private Object target;public LoggingInvocationHandler(Object target) {this.target = target;}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before method call");Object result = method.invoke(target, args);System.out.println("After method call");return result;}
}// 使用示例
Service realService = new RealService();
Service proxy = (Service) Proxy.newProxyInstance(realService.getClass().getClassLoader(),new Class<?>[]{Service.class},new LoggingInvocationHandler(realService)
);proxy.doSomething();
  • 动态代理本质上是 对方法调用的拦截与增强
  • 它并不是直接“传入一个函数,返回一个增强函数”,而是 对对象的方法调用做拦截处理
  • 但其背后的思想确实是:传入一个行为(目标对象),返回一个包装后的行为(代理对象)

Go 语言的“函数增强”方式:高阶函数

Go 没有动态代理的概念,但它可以通过 函数闭包 + 高阶函数 轻松实现类似功能。

package mainimport ("fmt""time"
)// 增强器函数:接收一个函数,返回一个新的增强函数
func timeLogger(fn func()) func() {return func() {start := time.Now()fn()fmt.Printf("Function took %v\n", time.Since(start))}
}func sayHello() {fmt.Println("Hello, World!")
}func main() {wrapped := timeLogger(sayHello)wrapped()
}
  • 直接操作函数,不需要类、接口、代理;
  • 更加轻量、直观;
  • 更容易写出通用中间件(如 HTTP Middleware);

Java 代码执行流程详解

1. Java 内存由 JVM 管理,主要分为以下区域:

1. 堆(Heap)
  • 所有对象实例和数组存储在此,是 JVM 管理的最大内存区域。
  • 线程共享,垃圾回收(GC)的主要区域。
  • 分代设计
    • 新生代(Young Generation):新创建的对象在此,分为 Eden 区和两个 Survivor 区。
    • 老年代(Old Generation):长期存活的对象(多次 GC 后仍存活)进入老年代。
    • 永久代 / 元空间(PermGen/Metaspace):JDK 8 之前存储类元数据(如类结构、方法字节码),JDK 8 后改为元空间(直接内存)。
2. 栈(Stack)
  • 线程私有,每个线程有独立的栈。
  • 存储局部变量方法调用帧(包含方法参数、返回值、局部变量表、操作数栈等)。
  • 栈帧(Stack Frame):每个方法调用创建一个栈帧,方法执行完毕后栈帧弹出。
3. 方法区(Method Area)
  • 线程共享,存储类的结构信息(如类名、字段、方法、常量池)。
  • JDK 8 之前是堆的永久代(PermGen),JDK 8 后改为元空间(Metaspace),使用直接内存。
4. 本地方法栈(Native Method Stack)
  • 为 ** 本地方法(如native关键字修饰的方法)** 服务,与 Java 栈类似。
5. 程序计数器(Program Counter Register)
  • 线程私有,记录当前线程执行的字节码行号,用于线程切换后恢复执行位置。

2. 程序启动:从 main() 方法开始

  • JVM 调用 main() 方法作为程序入口。
  • main() 方法本身是在主线程中执行的。
public static void main(String[] args) {System.out.println("Start of program");someMethod();System.out.println("End of program");
}

3. 方法调用栈(Call Stack)

  • Java 使用 调用栈(call stack) 来管理方法调用。
  • 每次调用一个方法,JVM 都会在当前线程的栈中压入一个 栈帧(Stack Frame)
  • 栈帧包含局部变量表、操作数栈、动态链接、返回地址等信息。
void methodA() {methodB();
}void methodB() {int x = 10;
}

调用顺序如下:

main()
└─ methodA()└─ methodB()
  • methodB() 执行完后,弹出栈帧;
  • 返回到 methodA()
  • 最终回到 main(),继续执行后续代码。

4. 异常处理与 finally 块

  • 即使发生异常,JVM 也会确保栈帧被正确弹出。
  • finally 块保证无论是否抛出异常都会执行(除非虚拟机退出)。

    5. 对象创建与内存分配示例

    public class MemoryExample {public static void main(String[] args) {// 1. 基本类型变量存储在栈中int a = 10;// 2. 对象引用存储在栈中,对象实例存储在堆中Person p = new Person("Alice", 25);// 3. 调用方法时,方法帧入栈p.sayHello();}
    }class Person {private String name;  // 成员变量存储在堆中的对象实例中private int age;      // 成员变量存储在堆中的对象实例中public Person(String name, int age) {this.name = name;this.age = age;}public void sayHello() {// 局部变量存储在栈帧中String message = "Hello, my name is " + name;System.out.println(message);}
    }
    
    内存分析:
    1. 基本类型变量a:存储在main方法的栈帧中。
    2. 对象引用p:存储在main方法的栈帧中,指向堆中的Person对象实例。
    3. Person对象实例:存储在堆中,包含nameage字段。
    4. 方法调用p.sayHello():创建新的栈帧,存储局部变量message,方法执行完毕后栈帧销毁。


    Java 垃圾回收机制(GC)与内存释放

    java -XX:+PrintCommandLineFlags -version

    此命令会输出 JVM 的版本信息和启动参数,其中启动参数里就包含了选用的垃圾收集器等关键信息。

    在Java 8中,默认的垃圾收集器配置是使用Parallel GC,即新生代采用Parallel Scavenge收集器,而老年代则使用Parallel Old收集器。这种组合旨在提高应用程序的整体吞吐量。

    从Java 9开始,JVM默认使用的垃圾收集器变更为G1(Garbage First)收集器,它被设计为能够处理非常大的堆,并且可以在大多数情况下提供更短的停顿时间。G1收集器通过将堆划分为多个区域(Region),然后根据垃圾的数量优先回收那些包含最多可回收对象的区域,从而实现高效内存管理。

    对于之后的版本,例如Java 11和Java 17等长期支持(LTS)版本,默认依旧是G1收集器。不过,值得注意的是,在较新的Java版本中(比如Java 17或更新版本),随着ZGC和Shenandoah等低延迟垃圾收集器的发展和完善,用户可以根据需要选择这些先进的垃圾收集器作为替代方案,但它们并不是默认选项。

    1. Java 内存模型简述

    区域描述
    方法区存储类信息、静态变量、常量池等
    堆(Heap)对象实例主要存放地,GC 主要工作区域
    每个线程私有,存储局部变量、方法参数等
    本地方法栈用于 Native 方法调用
    程序计数器当前线程所执行字节码的行号指示器

    2. 垃圾回收的基本原理

    • Java 的垃圾回收(Garbage Collection, GC)自动管理堆内存。
    • 可达性分析算法(GC Roots Tracing) 是主流判断对象是否为垃圾的方式:
      • 从 GC Roots 开始遍历引用链;
      • 无法到达的对象被视为不可达,可被回收。

    常见的 GC Roots 包括:

    • 虚拟机栈中的局部变量引用;
    • 方法区中类的静态属性引用;
    • 方法区中常量引用;
    • 本地方法栈中 Native 引用。

    3. 方法执行结束后是否会释放内存?

     局部变量(基本类型):
    • 存储在栈中,方法执行完毕自动弹出栈帧,无需 GC。
     局部变量引用的对象(如 new Object()):
    • 实际对象在堆中;
    • 方法执行结束后,该引用不再存在(即离开作用域);
    • 如果没有其他引用指向该对象,则下一次 GC 会将其回收。
    void method() {List<String> list = new ArrayList<>();list.add("Hello");
    }
    • 方法执行完后,list 变量消失;
    • 如果没有其他引用指向这个 ArrayList,GC 会回收它。

    4. 如何加快对象回收?(建议做法)

    • 将不再使用的对象设为 null(帮助 GC 更早识别无用对象);
    • 避免内存泄漏(如长生命周期对象持有短生命周期对象的引用);
    • 使用弱引用(WeakHashMap、SoftReference、PhantomReference)来辅助 GC 工作。

    总结

    项目内容
    最小执行单元线程(Thread)
    方法的作用逻辑封装单位,必须在线程中执行
    方法调用流程通过调用栈进行压栈、弹栈操作
    方法执行结束栈帧自动弹出,局部变量释放,堆中对象等待 GC
    垃圾回收机制自动管理堆内存,使用可达性分析算法
    内存释放时机对象不可达时,由 GC 在适当时间回收

    扩展

    垃圾回收的发展简要时间线:

    • 1958年    John McCarthy 在 Lisp 中实现第一个自动垃圾回收器
    • 1960年代~1970年代    多种函数式语言系统使用 GC 技术
    • 1983年    Smalltalk 使用 GC 管理对象内存
    • 1995年    Java 发布,GC 成为主流开发语言的重要组成部分
    • 2000年后    .NET、Python、Ruby、JavaScript、Go 等语言广泛采用 GC

    现在主流语言

    c/C++
    • 手工管理 malloc free/new delete
    • 忘记释放-memory leak-out of memory
    • 释放多次产生极其难易调试的bug,一个线程空间莫名其妙被另外一个释放了
    • 开发效率很低

    java python go js kotlin scala
    • 方便内存管理的语言

    • GC-GarbageCollector-应用线程只管分配,垃圾回收器负责回收

    • 大大减低程序员门槛

    rust
    • 运行效率超高(asmcc++)

    • 不用手工管理内存(没有GC)

    • 学习曲线巨高(ownership)

    • 你只要程序语法通过,基本就不会有bug

    http://www.xdnf.cn/news/712405.html

    相关文章:

  • spring boot 拦截器HandlerInterceptor 不生效的原因排查
  • TripGenie:畅游济南旅行规划助手:个人工作纪实(二十一)
  • Shortest path 代码
  • RV1126-OPENCV 交叉编译
  • vue发版html 生成打包到docker镜像进行发版
  • STM32F103_Bootloader程序开发05 - Keil修改生成文件的路径与文件名,自动生成bin格式文件
  • Unity3D仿星露谷物语开发55之保存游戏到文件
  • ubuntu20.04编译 pjproject-2.7.1
  • 删除并重新排队
  • Redis 主从复制中的全量拷贝机制详解
  • IBM DB2数据库管理工具IBM Data Studio
  • Ubuntu 安装 Miniconda 及配置国内镜像源完整指南
  • 源的企业级网络安全检测工具Prism X(棱镜X)
  • Linux:shell脚本常用命令
  • [智能算法]蚁群算法原理与TSP问题示例
  • 高空视角之大美遥感-基于Leaflet和天地图的壮美遥感影像实践
  • ASP.NET Core OData 实践——Lesson7使用Reference增删改查一对多Navigation Property(C#)
  • AU6815集成音频DSP的2x25W数字型ClaSS D音频功率放大器(替代TAS5805)
  • LabVIEW旋转机械智能监测诊断系统
  • 02.MySQL库的操作
  • 涨薪技术|0到1学会性能测试第90课-性能测试构建
  • 设计模式-发布订阅
  • Docker安装
  • SpringCloud基础知识
  • Unity 中 Update、FixedUpdate 和 LateUpdate 的区别及使用场景
  • AMBA-AHB仲裁机制
  • RabbitMQ备份与恢复技术详解:策略、工具与最佳实践
  • 如何在WSL的Ubuntu里面启动腾讯微搭
  • excel表格记账 : 操作单元格进行加减乘除 | Excel中Evaluate函数
  • 关于DDOS