我也想从零开始学JVM虚拟机

从零开始掌握 JVM JVM是Java设计者用于屏蔽多平台差异,基于操作系统之上的一个"小型虚拟机",正是因为JVM的存在,使得Java应用程序运行时不需要关注底层操作系统的差异。使得Java程序编译只需编译一次生成class字节码,即可在任何操作系统都可以以相同的方式运行。类加载器实现将编译后的class文件加载到内存,并转为为运行时区域划分的运行时数据结构,注意类加载器只能决定类的加载,至于能不能运行则是由来决定。 阅读详情

启动JVM

编写一个最简单的HelloWorld类,然后运行:

public class HelloWorld {
  
  public static void main (String[] args) throws Exception{
    System.out.println("HelloWorld");
    Thread.sleep(10000);
  }
}

在这里插入图片描述

采用最原始的方式运行Java文件,打开任务管理器我们可以看到一个java.exe,是的,这个就是Java虚拟机,当10秒过后,main方法执行结束,java.exe结束,虚拟机结束,所以当我们启动一个java的main方法的时候我们就启动了Java虚拟机。

接着我们同时运行两次HelloWorld这个java类。同时就出现了两个java.exe,没错,运行了两个main方法,所以同时启动了两个Java虚拟机。

在这里插入图片描述
具体来说就是运行了几个main方法就启动了几个java应用,也就启动了几个虚拟机,到这里我们先认识了java虚拟机究竟是个什么玩意。

Java跨平台

当我们编写了一个HelloWorld.java文件之后,我们进行编译成class文件
在这里插入图片描述
接着我们只需要把class文件放到机器上就可以运行,比如window或者linux。
在这里插入图片描述
那么这个时候JVM就是将我们的class文件加载了之后(怎么加载?)丢到了机器识别的二进制然后进行运行。
在这里插入图片描述
那么在这里因为是一次编写到处运行,那么这个时候我们就会下载不同的jdk,不同的机器识别的指令是不一样的,所以我们需要安装不同的jdk,以使用我们不同的JVM去加载同一个class文件,然后丢到不同的机器上去运行。
在这里插入图片描述

JVM的生命周期

package com.zero;

public class jvm_01 {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    try {
                        Thread.currentThread().sleep(i*10000);
                        System.out.println("我是Thread线程" + i*10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
        for (int i = 0; i < 100; i++) {
            System.out.println("我是main方法的" + i);
        }
    }
}

运行该程序,然后启动任务管理器查看
在这里插入图片描述
当main方法for循环打印结束后,虚拟机还没有退出,而是等到Thread这个线程运行完之后才退出虚拟机,因为在虚拟机中有两种线程,分别是守护线程和非守护线程,main方法是属于非守护线程,在虚拟机中,只要有非守护线程未结束,虚拟机都不会退出,由于mian方法中启动了一个匿名线程也是一个非守护线程,所以它没有结束虚拟机也不会结束。而垃圾回收线程就是守护线程,守护线程是会自动销毁的,当虚拟机中的非守护线程全部退出之后,守护线程也就自动销毁。

非守护线程可以当做是象棋中的将军,守护线程则是象棋中将军旁边的守护者,当将军死了之后,守护者就没有存在的必要了,自然就自己销毁了。

JVM内存

在了解JVM虚拟机内存之前,我们先了解一下操作系统的内存。
在这里插入图片描述
首先我们了解到操作系统的内存是这样分布的,而JVM内存就是存在于我们的操作系统中的内存的一部分。
在这里插入图片描述
JVM内存是放在操作系统中的堆内存中,操作系统中的栈内存是由操作系统管理的,而堆内存一般是由我们去管理的,所以JVM的内存应该放在了操作系统的堆中,方便我们自己管理。

接着我们了解JVM内存结构。

在这里插入图片描述

我们可以发现JVM的内存结构跟操作系统中的内存结构似乎一致?原来JVM的设计模型其实就是操作系统的设计模型了,在基于操作系统的角度,JVM实际上就是一个应用,一个线程。但是基于class文件来讲,JVM就是操作系统,而JVM内存结构中的方法区,在class文件看来,就是一块“硬盘内存”。

所以概括的说,JVM内存主要分 堆(heap)、栈(stack)、方法区。
在这里插入图片描述
栈内存分为了虚拟机栈跟本地方法栈

堆内存分为了新生代跟老年代,新生代分为了Eden区,From Survivor区、To Survivor区。

JVM运行时数据区

Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。图如下,如何理解划分?

简单来说就是当你喝一口水下去之后,水就分散到了各个器官比如肾、胃、肝等等 这些就好比于运行时数据区。
在这里插入图片描述

程序计数器:当前线程所执行的字节码的行号指示器,字节码解释器工作时通过改变这个计数器的值来选取下一条需要执行的字节码指令,大概就是一个计数器,里面存了下一次要执行哪一行代码。

并且由于Java虚拟机的多线程时通过线程轮流切换、分配处理器执行时间的方式来实现。那么为了线程切换后可以恢复到正确的执行位置,每条线程都必须要有一个单独的程序计数器,所以它属于一块线程私有的内存。

简单来说:有两个线程A,B同时执行这个class,那么线程A的程序计数器执行到了第9行,就会记录10,以便下一次执行,刚好线程B进来了,要执行第一行,那么线程B的计数器就是1,执行完线程B之后可能执行线程A,这个时候就知道线程A要执行到了第10行。

如果此时线程正在执行的是一个java方法,这个计数器记录的是正在执行的虚拟机字节码的指令的地址。

Java虚拟机栈:虚拟机栈描述的是Java方法执行的时候的线程内存模型,每个方法在被执行的时候,Java虚拟机都会创建一个栈帧,这个栈帧用于存储局部变量表、操作数栈、动态连接、方法出口等信息。

Java虚拟机以方法作为最基本的执行单元,“栈帧”则是用于支持虚拟机进行方法调用和方法执行背后的一种数据结构。其存储了方法的局部变量表、操作数栈、动态连接、方法返回地址和一些额外的附加信息。
在这里插入图片描述

  • 局部变量表:一组变量值的存储空间,用于存放方法参数和方法内部定义的局部变量。局部变量表的容量是以变量槽为最小单位的,一个变量槽可以存放一个32位以内的数据类型,也就是说每个槽基本应该能存放一个boolean、byte、char、short、int、float、reference或returnAddress类型的数据,当然reference类型表示的是一个对象的引用地址,在这并没有很明确的指出这种引用是怎么样的数据结构。

对于64位的数据类型,比如long类型以及double类型,Java虚拟机是以高位对齐的方式为其分配两个连续的变量槽空间的。

接着我们了解Java虚拟机是如何使用局部变量表的。Java虚拟机通过索引定位的方式来使用局部变量表,索引值的范围就是从0开始一直到局部变量表最大的变量槽数量,假设有10个int类型的数值,索引就是从0到9,如果访问的是32位数据类型的变量,索引值就是N,如果访问的是64位数据类型的值,索引值会同时使用N跟N+1。并且对于两个相邻的共同存放一个64位数据类型的两个变量槽,虚拟机时不允许单独访问其中的某一个的。

  • 操作数栈:一个数据运算的地方,大多数指令都在操作数栈运算,然后压栈。当一个方法刚刚开始执行的时候,这个方法的操作数栈是空的,在方法的执行过程中,会有各种字节码指令往操作数栈中写入跟提取内容,也就是入栈跟出栈操作。

  • 动态连接:在每个栈帧中都会包含一个该栈帧所属方法的引用,这个引用会指向运行时常量池,这个引用的目的是为了方法调用过程中的动态连接

  • 方法返回地址:方法退出之后栈帧的顶端都应该是当前退出方法的上层方法,上层方法的状态会随着本次方法的返回结果而改变,而方法返回地址这块区域就是为了用来帮助栈帧去恢复上层方法的状态。简单点:A方法调B方法,B方法在A方法上面,B方法执行结束,要去找A,并告诉他结果。

  • 本地方法栈

跟虚拟机栈差不多,虚拟机栈服务的是JVM执行的java方法,本地方法栈服务的对象是JVM执行的native方法。其实也是一个方法,只是这个方法可能是用c语言或者其他语言写的…….比如这种鬼东西?在这里插入图片描述

方法区

一个用于存储已经被虚拟机加载的类型信息、常量、静态变量、代码缓存等数据。

heap堆

虚拟机内存中最大的一块了,也是一块所有线程共享的一块内存区域,在虚拟机启动时创建,几乎所有的对象创建都存储在堆里面了。当然Java堆就是垃圾收集器管理的内存区域。

对象的创建

在这里插入图片描述
我们重点研究虚拟机中堆的知识点,首先要了解java对象的创建,我们都知道Java对象创建之后会分配到我们堆内存中的Eden区域中,那么对象所需的内存大小在类加载完成后就可以确定了,对象在堆内存中的存储布局可以划分为三个部分:对象头、实例数据和对齐填充。
那么对象的对象头就包括两类信息

  • 第一类是用于存储对象自身的运行时数据,比如哈希码、GC分代年龄。。。。。。等等,不说太多。这部分数据的长度在32位和64位的虚拟机中分别为32bit跟64bit。对象头
  • 第二类时类型指针,即对象指向它的类型元数据的指针,干什么用的?Java虚拟机通过这个指针确定该对象是哪个类的实例。

实例数据部分存储的是对象的有效信息,即我们在程序代码里面所定义的各种类型的字段内容,无论是从父类继承下来的或者是子类中定义的字段。

对象的第三部分是对齐填充,并不是必然存在,也没有特别的含义,仅仅起着占位符的作用。这是因为虚拟机的自动内存管理系统要求对象起始地址必须是8字节的整数倍,所以如果对象实例数据部分不是8的整数倍,那就第三部分来凑一下。。。

现在知道了对象的大小,就可以给对象分配内存空间了,给对象分配内存空间,其实就是在堆中划分出一块已经确定大小的内存块。

在这里插入图片描述
假设我们现在Eden区域有10M内存,并且在堆中的内存是绝对规整的(被使用的放在一边,已使用的放在另一边),如上图,如果现在内存中并没有对象,那么就会有一个指针指着起始位置。当给对象分配内存的时候(假设内存1M),那么指针就会移动一段与对象的大小相等距离。
在这里插入图片描述
这种分配方式就称为指针碰撞。

但如果Java堆中的内存并不是规整的,已被使用的内存和空闲的内存是相互交错的,那就没办法用指针碰撞的方式分配内存,那么虚拟机就必须维护一个列表,记录哪些内存块是可用的,在分配的时候从列表中找出一块足够大的空间划分给此次创建的对象的大小,并且更新列表的记录,这种分配方式被称为空闲列表。而选择哪种分配方式是由堆内存是否规整决定的,而堆内存是否完整又由所采用的垃圾回收器是否带有空间压缩整理的能力决定。

当然在分配内存的时候也会出现一个问题,在并发的情况下似乎并不是安全的。什么意思呢?假设我们现在使用指针碰撞的方式分配内存,线程A创建了一个对象是1M,在指针还来不及挪动指针去分配内存的时候,线程B也创建了一个对象,原来的指针又被使用来分配内存。

至于如何解决

  • 方案一:对分配内存空间的动作进行同步处理,性能较差
  • 方案二:把内存分配的动作按照线程划分在不同的空间之中进行,也就是每个线程在堆中预先分配一小块内存,这种称为本地线程分配缓冲(Thread Local Allocation Buffer, TLAB),哪个线程要分配内存,就在哪个线程的本地缓冲区中分配,只有当本地缓冲区用完了,分配新的缓存区时才需要同步锁定。

分代

我们都知道在新生代实行了分代,分别是Eden区,From Survivor(S0),To Survivor(S1),并且他们所占的比例是8:1:1,那么为什么要分代呢?其本质在于对象的生命周期不一样,所以要分代。因为98%的对象在minor GC的时候会被回收掉,什么是minor GC呢?创建的对象都会分配到Eden内存,当Eden内存不够的时候,就会触发一次minor GC,这个时候就会有98%的对象被回收掉,存活的会进入S0内存或者S1内存,S0跟S1总是有一块区域是空闲的,因此新生代实际可用的内存空间为90%的新生代空间。

在经过一次Minor GC之后,使用复制算法将这些仍然存活的对象复制到另外一块Survivor中,然后清理使用的Eden区域跟Survivor,并且将对象的年龄+1,当对象的年龄达到某个值时,这些对象就会进入老年代,当然对于一些较大的对象则时直接进入老年代。

结语

小编是一枚Java Coder,业余写文章,现主营微信公众号《Java患者》,喜欢的话关注我的公众号我们一起学习Java

在这里插入图片描述

【从零开始学习JVM | 第一篇】快速了解JVM 在探索现代软件开发的丰富生态系统时,我们不可避免地会遇到一个强大而神秘的存在——Java虚拟机(JVM)。作为Java语言最核心的组成之一,JVM已经超越了其最初的设计目标,成为一个多语言的运行平台,支撑着企业级应用、大数据处理、移动应用等众多领域的技术栈。JVM的独特之处在于它的架构设计,它不仅负责代码的执行,还包括了内存管理、垃圾回收、线程同步等高级功能,这些都是确保高效、稳定运行Java程序的关键因素。 阅读详情

相关推荐

《自己动手写Java虚拟机》的源代码实现,使用Go语言构建Java虚拟机。.zip

《自己动手写Java虚拟机》的源代码实现,使用Go语言构建Java虚拟机。

自己动手写Java虚拟机 (Java核心技术系列)_java版

序   有一本用go语言编写的《自己动手写Java虚拟机》的书籍,抛开性能不说,但这本书已经勾勒出虚拟机的模型,对于想真正了解虚拟机执行原理的小伙伴,无疑是雪中送炭,有人会说,我不如直接去研究真正的Java虚拟机,那才叫原汁原味,学习概念模型的虚拟机和真实的虚拟机肯定是有巨大差别的, 但是我想说,如果你真这么牛逼,也不需要看我的博客了,我个人觉得,人的能力精力是有限的,如果花较少的时间和精力,就能对虚拟机有一个大致的了解,等将来有能力,精力,毅力再去研究jdk源码的实现,不失为一种曲线救国的办法,首先,我对

黄裳博客 2633

微信DAT图片恢复全攻略:从文件路径到批量解密(附最新工具下载)

本文详细介绍了微信DAT图片恢复的全过程,从文件路径定位到批量解密操作,帮助用户高效恢复加密存储的图片。文章涵盖新旧版本微信的存储结构差异,推荐使用专业解密工具进行批量转换,并提供了提升恢复成功率的实用技巧和常见问题解决方案。

weixin_29325007的博客 455

自己动手写java虚拟机(第一话)

使用go语言,自己手写一个java虚拟机,体验造物主的赶脚~

高山仰止,景行行止 1306

C#实战:如何用Bartender 10.1实现动态打印与批量打印(附完整源码)

本文深入探讨了如何在C#项目中高效集成Bartender 10.1,实现企业级的动态打印与批量打印功能。文章提供了从环境配置、COM接口精讲、高可用打印服务类封装,到批量打印性能优化及生产环境部署的全套实战方案与源码,帮助开发者构建健壮、可维护的打印解决方案。

weixin_29290963的博客 435

Windows显卡TDR超时导致Unity崩溃?手把手教你修改注册表延长响应时间

本文详细解析了Windows显卡TDR超时导致Unity崩溃的问题,并提供了修改注册表延长响应时间的完整解决方案。通过调整TdrDelay和TdrDdiDelay键值,开发者可以有效避免D3D11设备丢失错误,提升Unity编辑器的稳定性。文章还包含显卡驱动优化、系统性能调优等实用技巧,帮助开发者彻底解决编辑器崩溃问题。

weixin_33737774的博客 620

【JVM】手写Java虚拟机-01 命令行工具

介绍 手写一个简单的Java虚拟机,参考了bugstack虫洞栈,声哥,《自己动手写Java虚拟机》,和JVM-Demo。 本系列文章尽可能按照保姆级呈现。 如果有任何问题和建议,请联系我。 环境 操作系统:Windows 10 IDE:IntelliJ IDEA 2019.1 x64 JDK:Java 11.0.8 项目管理工具:apache-maven-3.5.4 [下载] 配置环境 Java的安装网上很多教程,就不赘述了。 注意安装Maven时,解压好之后要修改系统路径。然后试试调用cmd的mvn -

锥栗的博客 1084

西门子S7-1200与汇川SV660F伺服Profinet通讯调试实战

在工业自动化领域,Profinet作为工业以太网标准协议,凭借其高实时性和灵活组网能力,已成为伺服驱动器与PLC通讯的主流选择。其核心原理是通过设备名识别、IP分配以及标准报文(如111速度报文、105定位报文)传输控制字与状态字,实现驱动器的精准控制。相比传统脉冲或CANOpen方案,Profinet直连省去了协议转换网关,降低了成本与故障点,尤其适用于绕线设备、包装机械等中小型单轴或多轴运动控制系统。本文以西门子S7-1200 PLC搭配汇川SV660F伺服为例,完整梳理了从GSDMD组态、InoDri

weixin_29184371的博客 222

自己动手java虚拟机_自己动手写Java虚拟机

目  录前言第章 命令行工具1.1 准备工作1.1.1 安装1.1.2 安装1.1.3 创建目录结构1.2 命令1.3 编写命令行工具1.4 测试本章代码1.5 本章小结第章 搜索文件2.1 类路径2.2 准备工作2.3 实现类路径2.3.1 接口2.3.22.3.32.3.42.3.52.3.62.4 测试本章代码2.5 本章小结第章 解析文件3.1 文件3.2 解析文件3.2.1 读取数据3....

weixin_31865183的博客 463

QuestaSim使用Xilinx库的设置

安装QuestaSim

miracle_eicont的专栏 3406

【我的Android进阶之旅】Android自定义View实现一个游戏摇杆,来实现监听遥动的方向或者监听摇动的角度的功能

一、需求描述 去年有个需求,需要使用自定义View实现一个游戏摇杆,来识别上下左右摇动的方向。 有点类似下面这样的王者荣耀游戏摇杆的效果 二、业界已有的参考资料 在下面这篇博客介绍了如何自定义游戏摇杆 2.1 博客介绍 Android自定义摇杆 2.1.1 功能 支持自适应大小 支持2个方向、4个方向、8个方向的摇动监听 支持摇动角度获取 可选回调模式 支持可摇动区域自定义 支持摇杆自定义 支持设置图片、色值、Shape图形 2.1.2 效果图 2.1.3 设置回调方式 setCallBac

字节卷动 1407

动手写java虚拟机_[JVM] - 一份<自己动手写Java虚拟机>的测试版

配置GOROOT(一般是自动的),配置GOPATH(如果想自己改的话)参照> 第一章 指令集和解释器生成了ch01.exe文件这里还生成了一个gopkgs.exe文件执行以上操作,这里说明:go开发java虚拟机实际上这段模拟的是命令行在安装好java JDK后的一些输入,比如查看java的version信息.这里已经在代码中写好了.main.go :package mainimport "...

weixin_42395725的博客 409

CKA-题库-简洁参考答案-反复练习20遍-必过!!!

新版CKA考试已于2020年9月1日正式上线! 考试模式:线上考试 考试时间:2小时 认证有效期:3年 软件版本:Kubernetes v1.19 重考政策:可接受1次重考 经验水平:中級 题目数量:17题 题库量小:随机的题目重复率极高 考试时可打开两个浏览器Tab,一个是考试窗口,一个用来查阅官方文档](https://kubernetes.io/docs/) 01 Task - 英文 Create a new ClusterRole named deployment-clusterrole tha.

随笔记录-分享&记忆 7315

我也想从零开始了解一下JVM虚拟机

微信公众号:Java患者专注Java领域技术分享启动JVM编写一个最简单的HelloWorld类,然后运行:publicclassHelloWorld{ publics...

ZeJava的博客 258

bacnet 官方协议栈 bacnet-stack-0.8.2关于router例程BACnet MS/TP BACnet IP调试说明

bacnet 官方协议栈 bacnet-stack-0.8.2关于router例程mstp,bacnet ip调试说明

shufawangzhang的博客 2222

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

从零开始学JVM:Java虚拟机入门指南

想象你写了一封中文信,需要让全世界的人都能看懂。这时你需要一个翻译,把中文翻译成英文、法文、日文等。JVM(Java 虚拟机)就是这样的翻译官,它把 Java 代码翻译成不同计算机能理解的语言,实现 "一次编写,到处运行"。 3. 本地方法栈(调用外部资源的 "翻译官") 作用:让 Java 调用 C/C++ 等本地代码 常见场景:操作硬件、数据库连接(如 JDBC) 4. 堆(对象的 "大型仓库")

小高的博客 912

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

学Java JVM:全面解析Java虚拟机

学习JVM是一个从理论到实践、从宏观到微观的渐进过程。理解JVM的工作原理,不仅能帮助你写出更高效、更稳定的Java代码,还能在遇到性能瓶颈时,提供有效的诊断和优化思路。希望本文能为你的Java技术栈添砖加瓦,开启JVM探索之旅的第一步。

一杯梅子酱的博客 870

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

【JVM】从零开始深度解析JVM

本篇博客给大家带来的是JVM的知识点, 重点在类加载和垃圾回收机制上.🐎文章专栏: JavaEE初阶🚀若有问题 评论区见❤如果你不知道分享给谁,那就分享给薯条.你们的支持是我不断创作的动力 .王子,公主请阅🚀1. JVM简介1.2 Taobao JVM(国产研发)1.3 JDK JRE JVM三者关系(经典面试题)2. JVM 运行流程★3. JVM运行时数据区(内存区域)3.1 堆(线程共享)3.2 Java虚拟机栈(线程私有)/本地方法栈。

薯条不要番茄酱的博客 3779

【从零开始学JVM】第三章_运行时数据区

【从零开始学JVM】第三章_运行时数据区

踢桃的成长之路 1704

【从零开始学JVM】第一章_JVM和java体系结构

JVM学习笔记,jvm和java体系结构

踢桃的成长之路 1326

从零开始学习JVM(一)--初识Java虚拟机

初步认识虚拟机与Java虚拟机

huangjhai的博客 606

从零开始,自己动手写Java虚拟机

从零开始,自己动手写Java虚拟机 自己动手写一个Java虚拟机 java虚拟机的作用 java虚拟机的组成 java虚拟机的开发 java虚拟机的测试

karizhang的专栏 1万+

三.从零开始JVM实战高手-JVM的内存结构

目前市面上已有太多的JVM相关教程和书籍,但是大部分偏理论,比较枯燥难懂,少有结合实际业务开发,站在项目开发的视角下去分析和讲解相关经验的教程;而本套教程会从零开始带着大家一步一步深入了解JVM底层原理,以及结合一些开发中的典型生产环境问题来进行实战剖析,并且几乎采用一步一图的方式进行讲解。通过核心理论和实战案例的结合,希望能对大家对JVM的理解和应用更上一层楼。

墨家巨子@俏如来 964

Java程序员想年后跳槽,对JVM没有深入的理解,我劝你还是别跳了

针对这样的需求,可以看一下《深入理解Java虚拟机》,这本书是国内学习 JVM 的必备书籍。添加图片注释,不超过 140 字(可选)添加图片注释,不超过 140 字(可选)添加图片注释,不超过 140 字(可选)添加图片注释,不超过 140 字(可选)添加图片注释,不超过 140 字(可选)添加图片注释,不超过 140 字(可选)添加图片注释,不超过 140 字(可选)添加图片注释,不超过 140 字(可选)添加图片注释,不超过 140 字(可选)1.7复合优先于继承.......

代码界的扛把子 869
上一篇: 对象的最后一次救赎
下一篇: 为什么要使用MyCat?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值