JVM的内存区域划分(Java中的数据存储等)

Java8-JVM内存区域划分白话解读 java作为一款能够自动管理内存的语言,与传统的c/c++语言相比有着自己独特的优势。虽然我们无需去管理内存,但为了防范可能发生的异常,我们需要对java内部数据如何存储有一定了解,已应对突发问题,写出更好的程序java程序在被编译成字节码后,由JVM执行,执行期间产生的所有数据,会被分门别类的存储在JVM预设好的区域里,具体情况如下所示以下为java8的内存分布图​Tips:红色是线程共享的,黄色是线程私有的接下来我们着重讨论Java8中的内存分布情况这部分内存在JVM中,由JVM直接分配,初始大小、最大 阅读详情

 由于Java程序是交由JVM执行的,所以我们在谈Java内存区域划分的时候事实上是指JVM内存区域划分。在讨论JVM内存区域划分之前,先来看一下Java程序具体执行的过程:

                                       

  如上图所示,首先Java源代码文件(.java后缀)会被Java编译器编译为字节码文件(.class后缀),然后由JVM中的类加载器加载各个类的字节码文件,加载完毕之后,交由JVM执行引擎执行。在整个程序执行过程中,JVM会用一段空间来存储程序执行期间需要用到的数据和相关信息,这段空间一般被称作为Runtime Data Area(运行时数据区),也就是我们常说的JVM内存。因此,在Java中我们常常说到的内存管理就是针对这段空间进行管理(如何分配和回收内存空间)。

一.运行时数据区包括哪几部分?

  根据《Java虚拟机规范》的规定,运行时数据区通常包括这几个部分:程序计数器(Program Counter Register)、Java栈(VM Stack)、本地方法栈(Native Method Stack)、方法区(Method Area)、堆(Heap)。

  如上图所示,JVM中的运行时数据区应该包括这些部分。在JVM规范中虽然规定了程序在执行期间运行时数据区应该包括这几部分,但是至于具体如何实现并没有做出规定,不同的虚拟机厂商可以有不同的实现方式。

二.运行时数据区的每部分到底存储了哪些数据?

1.程序计数器(寄存器)

  程序计数器(Program Counter Register),也有称作为PC寄存器。想必学过汇编语言的朋友对程序计数器这个概念并不陌生,在汇编语言中,程序计数器是指CPU中的寄存器,它保存的是程序当前执行的指令的地址(也可以说保存下一条指令的所在存储单元的地址),当CPU需要执行指令时,需要从程序计数器中得到当前需要执行的指令所在存储单元的地址,然后根据得到的地址获取到指令,在得到指令之后,程序计数器便自动加1或者根据转移指针得到下一条指令的地址,如此循环,直至执行完所有的指令。

  虽然JVM中的程序计数器并不像汇编语言中的程序计数器一样是物理概念上的CPU寄存器,但是JVM中的程序计数器的功能跟汇编语言中的程序计数器的功能在逻辑上是等同的,也就是说是用来指示 执行哪条指令的。

  由于在JVM中,多线程是通过线程轮流切换来获得CPU执行时间的,因此,在任一具体时刻,一个CPU的内核只会执行一条线程中的指令,因此,为了能够使得每个线程都在线程切换后能够恢复在切换之前的程序执行位置,每个线程都需要有自己独立的程序计数器,并且不能互相被干扰,否则就会影响到程序的正常执行次序。因此,可以这么说,程序计数器是每个线程所私有的。

  在JVM规范中规定,如果线程执行的是非native方法,则程序计数器中保存的是当前需要执行的指令的地址;如果线程执行的是native方法,则程序计数器中的值是undefined。

  由于程序计数器中存储的数据所占空间的大小不会随程序的执行而发生改变,因此,对于程序计数器是不会发生内存溢出现象(OutOfMemory)的。

2.Java栈

  Java栈也称作虚拟机栈(Java Vitual Machine Stack),也就是我们常常所说的栈,跟C语言的数据段中的栈类似。事实上,Java栈是Java方法执行的内存模型。为什么这么说呢?下面就来解释一下其中的原因。

  Java栈中存放的是一个个的栈帧,每个栈帧对应一个被调用的方法,在栈帧中包括局部变量表(Local Variables)、操作数栈(Operand Stack)、指向当前方法所属的类的运行时常量池(运行时常量池的概念在方法区部分会谈到)的引用(Reference to runtime constant pool)、方法返回地址(Return Address)和一些额外的附加信息。当线程执行一个方法时,就会随之创建一个对应的栈帧,并将建立的栈帧压栈。当方法执行完毕之后,便会将栈帧出栈。因此可知,线程当前执行的方法所对应的栈帧必定位于Java栈的顶部。讲到这里,大家就应该会明白为什么 在 使用 递归方法的时候容易导致栈内存溢出的现象了以及为什么栈区的空间不用程序员去管理了(当然在Java中,程序员基本不用关系到内存分配和释放的事情,因为Java有自己的垃圾回收机制),这部分空间的分配和释放都是由系统自动实施的。对于所有的程序设计语言来说,栈这部分空间对程序员来说是不透明的。下图表示了一个Java栈的模型:

  局部变量表,顾名思义,想必不用解释大家应该明白它的作用了吧。就是用来存储方法中的局部变量(包括在方法中声明的非静态变量以及函数形参)。对于基本数据类型的变量,则直接存储它的值,对于引用类型的变量,则存的是指向对象的引用。局部变量表的大小在编译器就可以确定其大小了,因此在程序执行期间局部变量表的大小是不会改变的。

  操作数栈,想必学过数据结构中的栈的朋友想必对表达式求值问题不会陌生,栈最典型的一个应用就是用来对表达式求值。想想一个线程执行方法的过程中,实际上就是不断执行语句的过程,而归根到底就是进行计算的过程。因此可以这么说,程序中的所有计算过程都是在借助于操作数栈来完成的。

  指向运行时常量池的引用,因为在方法执行的过程中有可能需要用到类中的常量,所以必须要有一个引用指向运行时常量。

  方法返回地址,当一个方法执行完毕之后,要返回之前调用它的地方,因此在栈帧中必须保存一个方法返回地址。

  由于每个线程正在执行的方法可能不同,因此每个线程都会有一个自己的Java栈,互不干扰。

3.本地方法栈

  本地方法栈与Java栈的作用和原理非常相似。区别只不过是Java栈是为执行Java方法服务的,而本地方法栈则是为执行本地方法(Native Method)服务的。在JVM规范中,并没有对本地方发展的具体实现方法以及数据结构作强制规定,虚拟机可以自由实现它。在HotSopt虚拟机中直接就把本地方法栈和Java栈合二为一。

4.堆

  在C语言中,堆这部分空间是唯一一个程序员可以管理的内存区域。程序员可以通过malloc函数和free函数在堆上申请和释放空间。那么在Java中是怎么样的呢?

  Java中的堆是用来存储对象本身的以及数组(当然,数组引用是存放在Java栈中的)。只不过和C语言中的不同,在Java中,程序员基本不用去关心空间释放的问题,Java的垃圾回收机制会自动进行处理。因此这部分空间也是Java垃圾收集器管理的主要区域。另外,堆是被所有线程共享的,在JVM中只有一个堆。

5.方法区

  方法区在JVM中也是一个非常重要的区域,它与堆一样,是被线程共享的区域。在方法区中,存储了每个类的信息(包括类的名称、方法信息、字段信息)、静态变量、常量以及编译器编译后的代码等。

  在Class文件中除了类的字段、方法、接口等描述信息外,还有一项信息是常量池,用来存储编译期间生成的字面量和符号引用。

  在方法区中有一个非常重要的部分就是运行时常量池,它是每一个类或接口的常量池的运行时表示形式,在类和接口被加载到JVM后,对应的运行时常量池就被创建出来。当然并非Class文件常量池中的内容才能进入运行时常量池,在运行期间也可将新的常量放入运行时常量池中,比如String的intern方法。

  在JVM规范中,没有强制要求方法区必须实现垃圾回收。很多人习惯将方法区称为“永久代”,是因为HotSpot虚拟机以永久代来实现方法区,从而JVM的垃圾收集器可以像管理堆区一样管理这部分区域,从而不需要专门为这部分设计垃圾回收机制。不过自从JDK7之后,Hotspot虚拟机便将运行时常量池从永久代移除了。

  以上为个人看法和观点,如有不正之处希望谅解并欢迎指正。

 

程序运行时,我们最好对数据保存到什么地方做到心中有数。特别要注意的是内在的分配,有六个地方都可以保存数据:
1、 寄存器。这是最快的保存区域,因为它位于和其他所有保存方式不同的地方:处理器内部。然而,寄存器的数量十分有限,所以寄存器是根据需要由编译器分配。我们对此没有直接的控制权,也不可能在自己的程序里找到寄存器存在的任何踪迹。
2、 堆栈。驻留于常规RAM(随机访问存储器)区域。但可通过它的“堆栈指针”获得处理的直接支持。堆栈指针若向下移,会创建新的内存;若向上移,则会释放那些内存。这是一种特别快、特别有效的数据保存方式,仅次于寄存器。创建程序时,Java编译器必须准确地知道堆栈内保存的所有数据的“长度”以及“存在时间”。这是由于它必须生成相应的代码,以便向上和向下移动指针。这一限制无疑影响了程序的灵活性,所以尽管有些java数据要保存在堆栈里——特别是对象句柄,但java对象并不放到其中。
3、 堆。一种常规用途的内存池(也在RAM区域),其中保存了java对象。和堆栈不同:“内存堆”或“堆”最吸引人的地方在于编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要在堆里停留多长的时间。因此,用堆保存数据时会得到更大的灵活性。要求创建一个对象时,只需用new命令编制相碰的代码即可。执行这些代码时,会在堆里自动进行数据的保存。当然,为达到这种灵活性,必然会付出一定的代价:在堆里分配存储空间时会花掉更长的时间
4、 静态存储。这儿的“静态”是指“位于固定位置”。程序运行期间,静态存储的数据将随时等候调用。可用static关键字指出一个对象的特定元素是静态的。但java对象本身永远都不会置入静态存储空间。
5、 常数存储。常数值通常直接置于程序代码内部。这样做是安全的。因为它们永远都不会改变,有的常数需要严格地保护,所以可考虑将它们置入只读存储器(ROM)。
6、 非RAM存储。若数据完全独立于一个程序之外,则程序不运行时仍可存在,并在程序的控制范围之外。其中两个最主要的例子便是“流式对象”和“固定对象”。对于流式对象,对象会变成字节流,通常会发给另一台机器,而对于固定对象,对象保存在磁盘中。即使程序中止运行,它们仍可保持自己的状态不变。对于这些类型的数据存储,一个特别有用的技艺就是它们能存在于其他媒体中,一旦需要,甚至能将它们恢复成普通的、基于RAM的对象。

 

首先,java里面是没有静态变量这个概念的,不信你自己在方法里面定义一个static int i =0;java里只有静态成员变量。它属于类的属性。虚拟机的体系结构:堆,方法区,本地方法栈,pc寄存器。而方法区保存的就是一个类的模板,堆是放类的实例的。栈是一般来用来函数计算的。随便找本计算机底层的书都知道了。栈里的数据,函数执行完就不会存储了。这就是为什么局部变量每一次都是一样的。就算给他加一后,下次执行函数的时候还是原来的样子。











基于STM32的智能衣柜系统开发实战(附完整源码与硬件设计) 本文详细介绍了基于STM32的智能衣柜系统开发实战,包括硬件设计、软件架构和云端功能实现。系统具备实时温湿度监控、自动加热除湿、紫外线杀菌等核心功能,通过STM32F103C8T6主控芯片和ESP8266无线模块实现智能控制。附完整源码与硬件设计,适合科技爱好者和开发者参考。 阅读详情

相关推荐

科研笔记:怎样写好博士论文?

博士论文外审避坑指南与写作要点 博士论文外审的核心关注点呈现5级优先级:科研成果(硬指标)>创新性>逻辑结构与场景合理性>文献综述与规范性>工作量验证。外审专家会重点核查:1)成果清单是否达标;2)创新点是否真实明确;3)章节逻辑是否自洽、应用场景是否细化;4)文献综述是否构建"研究坐标系";5)实验/理论支撑是否充分。需特别注意三大雷区:科学错误(直接挂稿)、伪创新、场景不合理。写作时应强化选题依据的场景细化,通过"发展阶段+跨领域对比"提升文献综述质量。

Grow into a more wonderful you 1064

JVM的内存区域划分,对象实例化分析

由于Java程序是交由JVM执行的,所以我们在谈Java内存区域划分的时候事实上是指JVM内存区域划分。在讨论JVM内存区域划分之前,先来看一下Java程序具体执行的过程:                                           如上图所示,首先Java源代码文件(.java后缀)会被Java编译器编译为字节码文件(.class后缀),然后由JVM中的类加载

每天迈出一小步! 2603

PointMLP实战:5分钟用PyTorch搭建点云分类模型(附GitHub代码)

本文提供了基于ICLR 2022论文PointMLP的点云分类模型实战教程。通过PyTorch框架,详细介绍了从环境搭建、数据预处理、模型架构解析到训练推理的全流程,并分享了性能调优技巧。该模型利用残差MLP模块构建,结构清晰高效,适合开发者快速上手并应用于实际点云处理项目。

r2s3t4的博客 215

JVM内存分析

JVM实际内存占用超过Xmx的原因,设置Xmx的技巧:https://blog.youkuaiyun.com/begefefsef/article/details/126411842。聊聊jvm的CompressedClassSpace:https://blog.youkuaiyun.com/li123128/article/details/88928392。史上最全的JVM配置参数大全:https://blog.youkuaiyun.com/weixin_41883161/article/details/129557331。

yyc0227的博客 1316

R语言实战:非正态数据下的Scheirer–Ray–Hare检验全流程解析

本文详细解析了在R语言中执行Scheirer–Ray–Hare检验的全流程。当实验数据不符合正态分布时,该非参数检验是分析两因素设计(包括主效应和交互效应)的有效工具。文章从原理、环境配置、数据导入、核心代码到结果解读与报告撰写,提供了完整的实战指南,并附有常见的避坑建议。

weixin_29061465的博客 398

WorkVisual传输程序报错_发现一个内部错误

WorkVisual传输程序报错_发现一个内部错误

qq_16401025的博客 2455

【JVM】堆内存分析,常见内存溢出,GC日志分析轻重GC,jstack,jcmd,JMX,死锁,jmap,CMS,JVM常见参数

【JVM】堆内存分析,常见内存溢出,GC日志分析轻重GC,jstack,jcmd,JMX,死锁,jmap,CMS,JVM常见参数

qq120631157的博客 4201

一步步指导:在Chrome中安装Vue插件vue-devtools

Vue.js是现代Web开发中最流行的前端框架之一,它以其轻量级、高性能和易于理解的设计理念受到开发者的喜爱。为了更高效地开发和调试Vue应用,Vue.js官方提供了一个强大的开发工具——vue-devtools。

小透明的博客 5619

JVM--内存模型、垃圾回收、常见面试题、JVM调优

2:进入TLAB区:如果TLAB区域足够装下对象则直接进入,如果装不下则根据refill_waste(JVM运行时动态维护的一个变量)会有两种情况:1:请求对象大于refill_waste时将当前TLAB区域剩余的区域用dump object填满然后新开辟一块TLAB区域存放该对象;根可达算法:从根元素(栈里引用的对象、本地方法栈引用的对象、常量池内对象、静态变量引用的对象、.class对象)开始寻找,凡是通过根元素可以搜索到的对象,都不是垃圾。大对象:Eden区分配不下;

u012218309的博客 1171

带你了解如何分析Jmeter监听器常用图表并定位瓶颈

1.1 Jmeter监听器常用图表 目录 1.1 Jmeter监听器常用图表 1.1-1 查看结果树 1.1-2 汇总报告 1.1-3 集合报告 1.1-4 综合图 1.1-5 断言结果 1.1-6 图形结果 1.1-7 响应时间图 1.2 Jmeter性能瓶颈定位 1.2-1 性能瓶颈产生的原因 1.2-2 性能瓶颈的解决方法 1.2-3 性能瓶颈如何定位 1.1-1 查看结果树 查看结果树:调试利器。可以查看每个请求具体的详情,方便进行测试的比对。有点类似我...

weixin_49029722的博客 4450

我每天如何使用 ChatGPT

我偶尔会用这个功能作为开始。我本人非常喜欢阅读,让 ChatGPT 来概括所有文章,感觉就像身处于但丁描述的地狱的某个层次那样痛苦。然而,有时一些文章实在是太啰嗦、无聊,或者写得让人不愉快(比如一个无趣记者对一个让人讨厌的人进行的漫无焦点的采访)。我需要获取这些文章中的一些信息,以便保持信息的更新和了解。在晚期资本主义的广告经济下,很多文章只在一个段落里提供新信息,其余都是无聊的填充内容,只是为了展示糟糕的广告,这实在是对人们的时间和生命的一种浪费。我有几次利用 ChatGPT 来。

KevinDai007的博客 3783

JVM内存性能调优思路之:通过GC log、Thread Dump 、Heap Dump分析内存使用说明

JVM内存性能调优思路之:通过GC log、Thread Dump 、Heap Dump分析内存使用说明

hiliang521的博客 1708

InvocationException: GraphViz‘s executables not found【BUG已解决】

在机器学习框架中遇到了一个bug:安装了GraphViz模块后,在对着sklearn的决策树文档操作输出决策树模型结果时,还是爆出了如下的错误: 报错位置为: 报错语句为:网上很多答案告诉我们,要我们安装GraphViz模块,然而并不对! 如下,我安装了GraphViz,但是还是报错了。 所以说问题不在这,我查阅质料发现,改正这个bug的方法有一个:下文详细描述。解决bug为: 1.去graphviz.官方下载自己电脑符合的操作系统的安装包:2,安装的时候,记住自己安装在电脑里的位置:以我的电脑为例,我的

一键难忘的博客 3968

JVM的内存区域划分

                   JVM的内存区域划分   学过C语言的朋友都知道C编译器在划分内存区域的时候经常将管理的区域划分为数据段和代码段,数据段包括堆、栈以及静态数据区。那么在Java语言当中,内存又是如何划分的呢?   由于Java程序是交由JVM执行的,所以我们在谈Java内存区域划分的时候事实上是指JVM内存区域划分。在讨论JVM内存区域划分之前,先来看一...

A15981957116的博客 146

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

jvm的内存区划分

原文地址:http://www.cnblogs.com/dolphin0520/p/3613043.html JVM的内存区域划分   学过C语言的朋友都知道C编译器在划分内存区域的时候经常将管理的区域划分为数据段和代码段,数据段包括堆、栈以及静态数据区。那么在Java语言当中,内存又是如何划分的呢?   由于Java程序是交由JVM执行的,所以我们在谈Java内存区域划分的时候...

SS413413的博客 189

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

转:JVM的内存区域划分

原文:https://www.cnblogs.com/dolphin0520/p/3613043.html JVM的内存区域划分   学过C语言的朋友都知道C编译器在划分内存区域的时候经常将管理的区域划分为数据段和代码段,数据段包括堆、栈以及静态数据区。那么在Java语言当中,内存又是如何划分的呢?   由于Java程序是交由JVM执行的,所以我们在谈Java内存区域划分的时候事实...

weixin_30547797的博客 72

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

5-JVM的内存区域划分

        JVM的内存区域划分 原文:https://www.cnblogs.com/dolphin0520/p/3613043.html   学过C语言的朋友都知道C编译器在划分内存区域的时候经常将管理的区域划分为数据段和代码段,数据段包括堆、栈以及静态数据区。那么在Java语言当中,内存又是如何划分的呢?   由于Java程序是交由JVM执行的,所以我们在谈Java内存区域划分的时...

犟驴的博客 144

jvm内存区域

一、简介 这张图就是JVM的内存区域,整个可以叫运行时数据区,绿色部分属于线程共享区,橙色为线程独占区 线程独占区 二、程序计算器 它是一块较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器。 如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址(也就是‘行号’);如果正在执行的是Natvie方法,这个计数器值则为空(Undefined)。 此内存...

m0_38048304的博客 192

进程内存监控与提交内存统计:用Python定位内存瓶颈的实用方案

在系统性能优化与故障排查中,内存监控始终是绕不开的核心议题。物理内存、虚拟内存与提交内存这几个概念常被混淆,而真正决定系统稳定性的往往是提交内存——操作系统承诺给进程的虚拟内存总量,它直接影响页面文件增长与整体内存压力。理解提交内存的原理,是精准定位进程内存泄漏、资源异常占用的前提。通过跨平台进程监控工具,按提交内存排序即可快速识别高消耗进程,尤其适合老旧电脑日常巡检、服务器内存问题排查及开发阶段的内存泄漏验证。本文提供一套基于Python与psutil的轻量级实现,结合Windows与Linux环境的适配

weixin_34406086的博客 406

java中数据类型在内存中的存储方式

1.java是如何管理内存的 java的内存管理就是对象的分配和释放问题。(其中包括两部分) 分配:内存的分配是由程序完成的,程序员需要通过关键字new为每个对象申请内存空间(基本类型除外),所有的对象都在堆(Heap)中分配空间。 释放:对象的释放是由垃圾回收机制决定和执行的,这样做确实简化了程序员的工作。但同时,它也加重了JVM的工作。因为,GC为了能够正确释放对象,GC必须监控每一个对

斗code、年华 565

Java中的各种数据类型在内存中存储的方式

1.Java是如何管理内存的 java的内存管理就是对象的分配和释放问题。(其中包括两部分) 分配:内存的分配是由程序完成的,程序员需要通过关键字new为每个对象申请内存空间(基本类型除外),所有的对象都在堆(Heap)中分配空间。 释放:对象的释放是由垃圾回收机制决定和执行的,这样做确实简化了程序员的工作。但同时,它也加重了JVM的工作。因为,GC为了能够正确释放对象,GC必须监控

时无英雄、使竖子成名 617

Jvm 内存区域详解

Jvm 内存区域划分 运行时数据区:Java程序启动时系统内存分配的一块独立的内存区域,其中可分为两部分:线程共享部分和线程独享部分,那么下面便根据这样的划分规则来介绍一下各个内存区域的作用以便后面提到的Java运行原理做一下准备。 首先线程共享部分顾名思义,多个线程都可以访问的内存区域,其中包含方法区和堆内存,既然是内存区域那么肯定使用存放数据的,那么这两块内存区域到底是用来存放什么的呢?...

gg_fighting_yang的博客 323

java内存管理1

JAVA 内存管理总结 1. java是如何管理内存的  Java的内存管理就是对象的分配和释放问题。(两部分)  分配 :内存的分配是由程序完成的,程序员需要通过关键字new 为每个对象申请内存空间 (基本类型除外),所有的对象都在堆 (Heap)中分配空间。 释放 :对象的释放是由垃圾回收机制决定和执行的,这样做确实简化了程序员的工作。但同时,它也加重了JVM的工作。因为,

qieuyg88的专栏 240
上一篇: Java基础总结——常用的关键字解析(this,super,instanceof,final,static)
下一篇: Java的接口与抽象类
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值