精华内容
下载资源
问答
  • 2022-04-10 16:31:54

    1.JVM内存布局组成:

    包含堆区(Heap),元数据区(Metaspace),虚拟机栈(JVM Stack),本地方法栈(Native Method Stacks),程序计数器(Program Counter Register)

    2.堆(Heap):

    1)概括说包含了新生代和老年代,其中新生代又包含了Eden, Survivor(from, to)。每次Minor GC,会将Eden和Survivor from中存活的对象复制到Survivor to中。

    2)晋升老年代条件是age为15,大对象直接进入老年代

    3)-Xms(初始堆大小) -Xmx(最大堆大小) 一般设置为相同,防止堆空间不断扩容和回缩带来的系统压力

    3.虚拟机栈(JVM Stack):

    1)每个线程都有自己独有的虚拟机栈,线程执行每个方法都对应一个栈帧,方法开始执行对应栈帧的压入,方法执行对应栈帧的弹出。

    2)栈帧包含了局部变量表,操作数栈,动态连接和方法返回地址

    局部变量表:存放方法执行过程中的定义的局部变量,包括了方法参数

    操作数栈:简单理解为Java为了考虑跨平台性,没有选择寄存器,而通过操作数栈进行运算操作。

    虚拟机栈的默认大小都是1M,可以手动更改大小-Xss 1m

    栈溢出:StackOverflowError

    4.元数据区(Metaspace):

    JDK8取消了永久代,其中字符串常量移动到堆内存,其他的例如类的元信息,字段,静态属性,方法,常量移到元空间。

    5.程序计数器:

    简单理解为记录下一条JVM执行的执行地址。下图的解释,java生成的字节码要经过解释器得到机器码去执行,当执行第一条指令getstatic #20时,就会把下一条指令的地址3存入程序计数器,这样第一条指令执行完成后,就会去程序计数器取下一条命令的地址,执行下一条命令。

     

    参考:

    《面试八股文》之 JVM 20卷

    黑马程序员JVM完整教程,全网超高评价,全程干货不拖沓_哔哩哔哩_bilibili

    更多相关内容
  • 一、go语言内存布局 想象一下,你有一个如下的结构体。 代码如下: type MyData struct {  aByte byte  aShort int16  anInt32 int32  aSlice []byte } 那么这个结构体究竟是什么呢? 从根本上说,它描述了...
  • (1)派生类完全拥有基类的内存布局,并保证其完整性。 派生类可以看作是完整的基类的Object再加上派生类自己的Object。如果基类中没有虚成员函数,那么派生类与具有相同功能的非派生类将不带来任何性能上的差异。...
  • 以下是对C语言中的内存布局进行了详细的分析介绍。需要的朋友可以过来参考下
  • 介绍了C# Struct的内存布局问题解答,有需要的朋友可以参考一下
  • 数组定义和内存布局
  • C++ 内存对象布局

    2018-11-08 17:43:47
    涉及各种情况下C++对象的sizeof大小,包括单一类对象,继承,重复继承 多继承 单一虚继承 等各种情况下的对象大小。对C++对象内存布局有清楚了解。
  • 本文主要讲解C++对象模型中的菱形继承的对象模型,分别讨论基类对象变量和函数的继承问题。... 参考内容 VS2017 查看类内存布局 C++对象模型和布局(三种经典类对象内存布局) C++中菱形继承的基本概念及内存占用问题

    本文主要讲解C++对象模型中的菱形继承的对象模型,分别讨论基类对象变量和函数的继承问题。
    何为菱形继承:
    菱形继承是指一个基类(Base)派生出两个派生类(Derived1,Derived2),然后这两个派生类(Derived1,Derived2)派生出一个最终的派生类,如1.1的下图所示。

    一、菱形继承之非虚继承

    1.1类Base、派生类Derived1、派生类Derived2、最终派生类DDerived的UML结构图

    在这里插入图片描述

    1.2类Base、派生类Derived1、派生类Derived2、最终派生类DDerived的代码定义

    NonVirtualDerivedDiamondClass.cpp

    #include <iostream>
    
    using namespace std;
    
    class Base
    {
    public:
    	Base(int x) : x(x) {}
    
    protected:
    	int x;
    
    };
    
    class Derived1 : public Base
    {
    public:
    	Derived1(int y1) : Base(1), y1(y1) {}
    
    protected:
    	int y1;
    };
    
    class Derived2 : public Base
    {
    public:
    	Derived2(int y2) : Base(1), y2(y2) {}
    
    protected:
    	int y2;
    };
    
    class DDerived : public Derived1, public Derived2
    {
    public:
    	DDerived(int z) : Derived1(11), Derived2(22), z(z) {}
    	void callX()
    	{
    		cout << this->x << endl;
    	}
    
    protected:
    	int z;
    };
    

    1.3最终派生类DDerived对象模型

    由于最终的派生类包含了基类Base、派生类Derived1,Derived2的对象模型,因此只分析最终派生类DDerived对象模型即可。
    VS2017开发者模式查看C++对象模型方法可以参考这篇博客:C++单继承类对象内存布局实战讲解和分析

    • DDerived在内存中的布局
      在这里插入图片描述
      由上图可知,菱形继承派生类Derived1和Derived2对象的内存中各自继承和保存基类Base的成员变量int x;。当我们在最终派生类DDerived上调用成员变量x时,会出现歧义,DDerived不知道调用那个类对象的x。此时编译会报错,成员变量x调用歧义,如下图所示。
      在这里插入图片描述
      如果我们要在最终派生类DDerived调用继承而来的x,那么就要显示指定调用的作用域(“::”)限定符,指明是调用哪个基类继承过来的x,即this->Derived2::x,如下代码所示:
    class DDerived : public Derived1, public Derived2
    {
    public:
    	DDerived(int z) : Derived1(11), Derived2(22), z(z) {}
    	void callX()
    	{
    		cout << this->Derived2::x << endl; // 显示指定作用域Derived2::x,调用Derived2的成员变量x
    	}
    
    protected:
    	int z;
    };
    

    从DDerived内存布局中可以看出,派生类Derived1和Derived2的类对象都各自保存了一份从基类Base继承而来的成员变量int x;这样不但会造成最终派生类DDerived获取变量x出现歧义,同时也会造成内存浪费。那么,是否有办法解决这些问题呢?答案是肯定的,那就是采用虚继承。

    二、菱形继承之虚继承

    2.1类Base、派生类Derived1、派生类Derived2、最终派生类DDerived的UML结构图

    在这里插入图片描述
    由上图可知,只有派生类Derived1和派生类Derived2继承类Base时采用虚继承,而最终派生类DDerived继承Derived1和Derived2时采用普通继承。即

    Derived1 : public virtual Base { ... };
    Derived2 : public virtual Base { ... };
    DDerived : public Derived1, public Derived2 { ... };
    

    2.2类Base、派生类Derived1、派生类Derived2、最终派生类DDerived的代码定义

    VirtualDerivedDiamondClass.cpp

    #include <iostream>
    
    using namespace std;
    
    class Base
    {
    public:
    	Base() = default;
    	Base(int x) : x(x) {}
    
    protected:
    	int x;
    
    };
    
    class Derived1 : public virtual Base
    {
    public:
    	Derived1(int y1) : Base(1), y1(y1) {}
    
    protected:
    	int y1;
    };
    
    class Derived2 : public virtual Base
    {
    public:
    	Derived2(int y2) : Base(1), y2(y2) {}
    
    protected:
    	int y2;
    };
    
    class DDerived : public Derived1, public Derived2
    {
    public:
    	DDerived(int z) : Derived1(11), Derived2(22), z(z) {}
    	void callX()
    	{
    		cout << this->Derived2::x << endl;
    	}
    
    protected:
    	int z;
    };
    

    2.3最终派生类DDerived对象模型

    由于最终的派生类包含了基类Base、派生类Derived1,Derived2的对象模型,因此只分析最终派生类DDerived对象模型即可。
    VS2017开发者模式查看C++对象模型方法可以参考这篇博客:C++单继承类对象内存布局实战讲解和分析

    • DDerived在内存中的布局 在这里插入图片描述
      由上图可知,最终派生类DDerived的对象模型中,派生类Derived1和派生类Derived2都没有产生一份基类Base的成员变量int x;的内存,而是多了一个虚指针。该虚指针分别指向各自的虚函数表。虚函数表中存放了变量x的偏移地址。通过该偏移地址派生类Derived1和派生类Derived2就可以获取变量x。此时最终派生类可以直接用this指针调用变量x而不会产生歧义,如下图所示。
      在这里插入图片描述
      因此,虚拟继承主要是继承基类成员变量的偏移地址,该偏移地址是保存在虚指针指向的虚函数表上,排列顺序为按照变量的声明顺序进依次排列。如下图所示:
      在这里插入图片描述
      该虚继承的类都没有虚函数,那么假如基类存在虚函数,那么虚继承后的菱形继承最终派生类的类对象模型是怎么样的呢?接下来继续分析和讨论。

    三、基类有虚函数的菱形继承之虚继承

    3.1类Base、派生类Derived1、派生类Derived2、最终派生类DDerived的UML结构图

    在这里插入图片描述
    由上图可知,基类Base和派生类Derived1、Derived2都有虚析构函数和一个虚函数vfun1();,说明这是一个继承中有虚函数的类,即非POD类型的类,内存对象不可逐字节拷贝memcpy(…)。

    3.2类Base、派生类Derived1、派生类Derived2、最终派生类DDerived的代码定义

    #include <iostream>
    
    using namespace std;
    
    class Base
    {
    public:
    	Base() = default;
    	virtual ~Base() {}
    	
    	Base(int x) : x(x) {}
    
    protected:
    	int x;
    
    private:
    	virtual void vfun1() = 0;
    };
    
    class Derived1 : public virtual Base
    {
    public:
    	Derived1(int y1) : Base(1), y1(y1) {}
    	virtual ~Derived1() {}
    	virtual void vfun1() override
    	{
    		cout << "virtual Derived1::vfun1()" << endl;
    	}
    
    protected:
    	int y1;
    };
    
    class Derived2 : public virtual Base
    {
    public:
    	Derived2(int y2) : Base(1), y2(y2) {}
    	virtual ~Derived2() {}
    	virtual void vfun1() override
    	{
    		cout << "virtual Derived2::vfun1()" << endl;
    	}
    
    protected:
    	int y2;
    };
    
    class DDerived : public Derived1, public Derived2
    {
    public:
    	DDerived(int z) : Derived1(11), Derived2(22), z(z) {}
    
    	virtual void vfun1() override
    	{
    		cout << "virtual DDerived::vfun1()" << endl;
    	}
    
    	void callX()
    	{
    		cout << this->x << endl;
    	}
    
    protected:
    	int z;
    };
    

    3.3最终派生类DDerived对象模型

    在这里插入图片描述
    图3-1 有虚函数的菱形继承之虚继承图
    在这里插入图片描述
    图3-2 没有虚函数的菱形继承之虚继承图
    由上图3-1和对比图3-2可知,有虚函数的菱形继承之虚继承的最终派生类DDerived对象模型跟没有虚函数的菱形继承之虚继承的最终派生类DDerived基本一样,差别只有一个,那就是基类Base多了一个虚指针,该虚指针指向DDerived自身的虚函数表。这个虚函数表跟单继承的虚函数表一样,里面存放的都是DDerived自身的虚函数或者继承而来的虚函数。虚函数表的定义规则是,先将基类虚函数表内容拷贝一份到DDerived自身虚函数表中,然后用DDerived自身的虚函数覆盖虚函数表中同名的虚函数。
    同理,当有静态成员函数和静态成员变量、普通成员函数时,DDerived的类内存模型也同样不受影响,具体代码博主就不贴出来了,留一个小作业各位读者自己验证。

    四、总结

    • 菱形虚继承后基类的成员变量只有一份内存,不会在派生类中拷贝一份同样的成员变量占内存;
    • 虚继承后派生类不会拷贝基类成员变量,而是产生一个虚指针指向自身的虚函数表,该虚函数表存放获取基类成员变量的偏移地址;
    • 虚继承中的类存在虚函数,跟没有虚函数的虚继承只有一个差别,那就是产生当前类的虚指针,该虚指针指向最终的派生类的虚函数表,该虚函数表存放最终派生类的所有替换后的虚函数或者继承而来的虚函数地址;
    • 只有非静态成员才占对象模型的内存;
    • 类对象的静态变量和静态函数都不占用对象模型的内存,存放在静态储存区;
    • 类对象的普通成员函数也不占用对象模型的内存,存放在普通数据区

    五、参考内容

    c++之菱形继承问题
    C++对象模型和布局(三种经典类对象内存布局)
    C++中菱形继承的基本概念及内存占用问题
    C++之继承(多重继承+多继承+虚继承+虚析构函数+重定义)
    《深度探索C++对象模型》 侯捷 page:83-134

    展开全文
  • C程序内存布局

    2022-03-19 21:14:41
    C程序内存布局

    1、C程序内存布局图

    在这里插入图片描述

    注意:

    • 这个4GB大小的内存(0x0000 0000~0xFFFF FFFF)不是系统的内存,而是进程的虚拟地址空间
    • 用户所看到和接触的都是该虚拟地址,并不是实际的物理内存地址。
    • 当一个程序被编译后,只有文本段和数据段。
    • 程序被编译后的可执行程序放在硬盘里,叫程序。
    • 程序运行后叫进程。
    • 只有在真正访问一个地址的时候才建立这个地址的物理映射。

    2、虚拟地址空间

    • 当创建一个进程时,操作系统会为该进程分配一个4GB的虚拟地址空间。(以32 位的操作系统为例,一个指针长度是 4 字节,4字节指针的寻址能力是从(0x0000 0000~0xFFFF FFFF)。
    • 每个进程都会有一个自己的 4GB 虚拟地址空间。这 4GB 的地址空间是“虚拟”的,并不是真实存在的。

    3、内存分类

    文本段(代码段)

    • 存放了程序代码的数据。
    • 编译出来的可执行文件的二进制代码。
    • 用来存放代码,如printf,+,=,等代码。

    数据段

    1)rodata段(文字常量区)

    定义:

    • rodata段(文字常量区):只读数据段

    例:

    int main()
    {
    	char *str = "hello";
    	*(str+1) = 'a';  //str[1] = 'a';
    }
    结果:segmentation fault;  //报段错误
    

    解析:
    因为“hello”是常量,存放在.rodata段,不可改变。

    注意:

    • str在执行时才有内存,因为它在栈区。

    2)data段

    定义:

    • 存放已经初始化的全局变量或static(静态)变量
    • 只要程序运行,所有.data区的数据都会一直存在,直到程序终止。

    例:

    想知道一段程序运行的多少次一下代码可行吗?
    func()
    {
    	int total = 0;
    	total++;
    }
    结果:不可行
    

    解析:

    • 因为total在栈区,每次调用都会重新分配一段内存空间,total的值就会不一样。

    正确的方法:

    func()
    {
    	static int total = 0;
    	total++;
    }
    

    解析:

    • 因为static是静态变量,静态变量放在.data区,total的值不会改变。

    3)bss段

    定义:

    • 存放未初始化的全局变量或初始化为0的全局变量或static变量。
    • 默认值为0。

    栈区

    定义:

    • 栈内存由编译器在程序编译阶段完成。
    • 函数返回后该函数的栈空间消失,所以函数中返回局部变量的地址都是非法的。
    • 栈区存放局部变量。
    • 堆区的空间是由下往上增长的。
    • 自动分配内存,{}内有效,离开{}自动释放。
    • 未初始化的值为随机值。
    • 未初始化的静态局部变量,其值为0

    堆区

    • 堆内存是在程序执行过程中分配的,用于存放进程运行中被动态分配的的变量。
    • 函数返回这段内存不会消失。
    • malloc分配的内存,自己管理,用完要free,否则内存泄漏。
    • 堆区的空间足由下往上增长的。
    • 内存里面的内容为随机值,一般用memset()清0。
    • malloc()动态类型分配内存,在内存中分配一段内存使用。

    例:

    #include <stdio.h>
    #include<stdlib.h>
    int main()
    {
    	char *ptr;
    	
    	ptr = malloc(10);  //分配10个字节内存空间
    	memset(ptr, 0, 10); //memset()清0
    
    	realloc(ptr 100);
    
    	free(ptr);  
    	p = NULL;
    	 
    }
    
    • free(p),是放弃了指针对这个内存的占用,放弃之后,内存的值会改写成随机值。但是指针本身并没有被删除!指针仍然指向原来的那块内存!因此在free掉指针后,还要把指针指向null,即p = NULL

    命令行参数区

    定义:

    • 命令行传递的参数:Is -l /home
      int main(int argc, char **argv)
    • 参数区放命令行传给main()函数的参数:argc,argv。

    系统空间

    定义:

    • 存放在整个内核的代码和所有的内核模块,用来内核空间执行 Linux 系统调用。

    总结

    • 参数区,堆区,栈区,数据段是用来存放数据的。

    4、局部变量和全局变量

    局部变量

    定义:

    • 花括号内所以定义的变量是局部变量,只能在本花括号使用。

    例:

    int main(void)
    {
            int stack_var;
            const int rodata_var = 50;
            static int s_var;
            char *ptr = "hello world";
            char arr[] = "hello";
    }
    

    未定义的局部变量,其值是随机值
    未初始化的静态局部变量,其值为0

    全局变量

    定义:

    • 在花括号外定义的变量,是全局变量。

    例:

    #include <stdio.h>
    
    int g_data_var = 20;
    int g_bss_var;
    static int s_bss_var;
    
    • 已初始化的全局变量,可以被所有其他c文件或本C文件中的函数调用,如果另一个c文件想调用全局变量,要用extern声明
      例:
    extern int g_data_var;
    
    • 未初始化的全局变量,其值为0,可以被所有其他c文件或本C文件中的函数调用

    • 未初始化的静态全局变量,其值为0,static关键字声明的函数或变量,只能被本C文件调用

    5、C程序内存分布图文例子

    在这里插入图片描述

    解析:

    • student1:未初始化的全局变量,在bss段
    • count:未初始化的static静态变量,在bss段
    • g_def_age:已初始化的全局变量,在data段
    • student2、*student3、*name:局部变量,在栈区
    • malloc:这段动态内存空间在堆区分配
    • “zhangsan”:常量在rodata段
    展开全文
  • c++类内存布局

    2022-04-01 10:42:11
    可知内存布局如下: 父类成员变量 子类成员变量 二、父类含有虚函数 可见内存布局如下: 虚函数表指针 父类成员变量 子类成员变量 三、虚基类 虚继承的情况下,对象a1的大小为12字节,是因为前...

    一、普通继承

    1、先获取到对象a1的地址,通过地址查看内存,如下:

    可知内存布局如下:

    父类成员变量
    子类成员变量

    二、父类含有虚函数

    可见内存布局如下:

    虚函数表指针
    父类成员变量
    子类成员变量

     三、虚基类

     虚继承的情况下,对象a1的大小为12字节,是因为前四字节是虚基表地址(区别于虚函数地址)。

    虚基表第二个四字节是虚基表变量的偏移量,

    也就是说虚基表地址要偏移8个字节才能找打虚基类的变量

    虚基表地址
    A1成员变量
    虚基类成员变量

    四、上述虚继承只是为了分析,实际中不会用,因为没什么意义,

    我们需要用的是下面的虚继承

    内存模型:

    虚基类指针指向最后一个成员
    A1成员变量
    虚基类指针指向A2的成员变量
    A2成员变量
    C对象成员变量
    Grand虚基类成员变量

     所以这里有个this指针偏移的问题。

    比如这里c1对象,不管是取Grand成员变量、A1成员变量、本身的成员变量,只需在c1的地址上+偏移即可,

    但要取A2的成员变量,要把this指针编程A2对象的地址,再偏移。

    看如下例子:

     c直接赋给a2,但他俩的地址并不相同。

    这是因为A2是第二个继承的类。

    展开全文
  • C++对象内存布局

    2013-05-01 14:47:23
    介绍C++对象在内存中是怎样分布的,有助于深层学习C++。
  • 看了这个内存布局图详解之后,对于C++的了解更加深刻了,之前不懂得一头雾水的东西全都清楚了。
  • Synchronized四种锁状态 在 Java 语言中,使用 Synchronized 是能够实现线程同步的,即加锁。并且实现的是悲观锁,在操作同步...要想清晰地了解锁升级的过程,首先需要我们掌握内存布局,很多公司会问到这样一个问题: O
  • 下图是一张32位x86架构上运行的Linux中进程标准的内存布局, 通过该图从上至下的简要分析Linux中进程的内存分布情况和各自的大致用途: 一、内核空间 对于32位X86架构上运行的Linux进程而言, 其虚拟地址空间...
  • 查看Base类的内存布局 在VS 2019开发者命令提示中输入: cl 虚函数.cpp /d1reportSingleClassLayoutBase 其中,虚函数.cpp 为源文件的文件名, 最后的Base为要查看的类 /* Base类 内存布局 */ class Base size(8): +...
  • 内存布局

    2014-06-19 15:40:49
    C语言九阳神功----内存布局图!学习C语言最重要的莫过于了解内存分布,只有研究到底层去,才能更好的学习C!
  • Java对象内存布局

    千次阅读 2020-11-23 15:06:29
    一、Java对象内存布局 Java对象的内存布局:对象头(Header)、实例数据(Instance Data)和对齐填充(Padding)。无论是32位还是64位的HotSpot,使用的都是8字节对齐。也就是说每个java对象,占用的字节数都是8...
  • Visual Studio Extension for C ++结构内存布局可视化 动机 在C ++中,结构布局可能会受到不同因素的影响。 为了生成面向高性能数据缓存的代码或减少结构内存占用,重要的是要在创建,更新,删除或调试代码的同一...
  • 下面小编就为大家带来一篇详谈C++中虚基类在派生类中的内存布局。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • 易语言分析数据类型内存布局源码,分析数据类型内存布局,func,ptoint,GlobalSize
  • C++类对象的内存布局

    千次阅读 多人点赞 2019-10-29 10:17:26
    1、C++类对象的内存布局 在C++的类对象中,有两种类的成员变量:static和非static,有三种成员函数:static、非static和virtual。那么,它们在C++的内存中是如何分布的呢? C++程序的内存格局通常分为四个区:...
  • 本篇文章是对C++中的Data Member内存布局进行了详细的分析介绍,需要的朋友参考下
  • C/C++内存布局

    千次阅读 2019-04-27 22:34:01
      下图是c/c++的进程的内存分布布局图,搞清楚内存布局对于理解一个程序是非常重要的。    一个程序运行起来,操作系统会给每个进程分配一个 4G 的程序地址空间,当然这都是虚拟地址空间,因为如果一个进程分 4G...
  • 主要给大家介绍了关于C++对象继承中内存布局的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面跟着小编来一起学习学习吧。
  • 博客《Cpp 对象模型探索 —— 含有虚基类的类的内存布局》的图片原文档,网址:https://blog.csdn.net/itworld123/article/details/102890062。
  • 使用VS查看类实例的内存布局图 因为 MSVC 下使用 /d1 reportAllClassLayout 开关可以很方便的查看内存布局图,可以查看虚表,内存占用大小等。 其使用方法是:首先确保电脑安装了 VS,我安装的是 VS2019,在 Win10 ...
  • c++ 多态内存布局分析

    千次阅读 2019-01-22 12:04:08
    上节对分析了多态的基本概念, 本节从内存布局的角度再来分析一下多态吧。 单一继承的内存布局 最开始我们分析了虚指针, 虚表, 但都只停留在单继承中, 现在我们在来扩展的探讨多重继承的内存布局 #include &...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 232,006
精华内容 92,802
关键字:

内存布局

友情链接: lab2slx.rar