当前位置:首页 > 话题广场 > 攻略专题 > 游戏问答

关于00000007b怎么解决,你需要知道这些「正点原子Linux连载」第三十二章U-Boot启动流程详解(一)

1)实验平台:定时原子Linux开发版。

2)摘自《正点原子I.MX6U嵌入式Linux驱动开发指南》

注意官方微信号公众号,获取更多信息:定时原子。

上一章详细分析了uboot的顶级Makefile,并整理了uboot编译过程。

本章我们来详细的分析一下uboot的启动流程,理清uboot是如何启动的。通过对uboot启动流程的梳理,我们就可以掌握一些外设是在哪里被初始化的,这样当我们需要修改这些外设驱动的时候就会心里有数。另外,通过分析uboot的启动流程可以了解Linux内核是如何被启动的。

32.1 链接脚本u-boot.lds详解

要分析uboot的启动流程,首先要找到“入口”,找到第一行程序在哪里。程序的链接是由链接脚本来决定的,所以通过链接脚本可以找到程序的入口。如果没有编译过uboot的话链接脚本为arch/arm/cpu。但是这个不是最终使用的链接脚本,最终的链接脚本是在这个链接脚本的基础上生成的。编译一下uboot,编译完成以后就会在uboot根目录下生成u-boot.lds文件,如图32.1.1所示:

图32.1.1 链接脚本

只有编译u-boot以后才会在根目录下出现u-boot.lds文件!

只有编译u-boot以后才会在根目录下出现u-boot.lds文件!

只有编译u-boot以后才会在根目录下出现u-boot.lds文件!

打开u-boot.lds,内容如下:

示例代码32.1.1 u-boot.lds文件代码

1 OUTPUT_FORMAT("elf32-littlearm","elf32-littlearm","elf32-littlearm")

2 OUTPUT_ARCH(arm)

3 ENTRY(_start)

4 SECTIONS

5{

6.=0x00000000;

7.= ALIGN(4);

8.text :

9{

10*(.__image_copy_start)

11*(.vectors)

12 arch/arm/cpu/armv7 (.text*)

13*(.text*)

14}

15.= ALIGN(4);

16.rodata :{*(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*)))}

17.= ALIGN(4);

18.data :{

19*(.data*)

20}

21.= ALIGN(4);

22.=.;

23.= ALIGN(4);

24.u_boot_list :{

25 KEEP(*(SORT(.u_boot_list*)));

26}

27.= ALIGN(4);

28.image_copy_end :

29{

30*(.__image_copy_end)

31}

32.rel_dyn_start :

33{

34*(.__rel_dyn_start)

35}

36.rel.dyn :{

37*(.rel*)

38}

39.rel_dyn_end :

40{

41*(.__rel_dyn_end)

42}

43.end :

44{

45*(.__end)

46}

47 _image_binary_end =.;

48.= ALIGN(4096);

49.mmutable :{

50*(.mmutable)

51}

52.bss_start __rel_dyn_start (OVERLAY):{

53 KEEP(*(.__bss_start));

54 __bss_base =.;

55}

56.bss __bss_base (OVERLAY):{

57*(.bss*)

58.= ALIGN(4);

59 __bss_limit =.;

60}

61.bss_end __bss_limit (OVERLAY):{

62 KEEP(*(.__bss_end));

63}

64.dynsym _image_binary_end :{*(.dynsym)}

65.dynbss :{*(.dynbss)}

66.dynstr :{*(.dynstr*)}

67.dynamic :{*(.dynamic*)}

68.plt :{*(.plt*)}

69.interp :{*(.interp*)}

70.gnu.hash :{*(.gnu.hash)}

71.gnu :{*(.gnu*)}

72.ARM.exidx :{*(.ARM.exidx*)}

73.gnu.linkonce.armexidx :{*(.gnu.linkonce.armexidx.*)}

74}

第3行为代码当前入口点:_start, _start在文件arch/arm/lib中有定义,如图32.1.2所示:

图32.1.2 _start入口

从图32.1.1可以看出,_start后面就是中断向量表,从图中的“.section ".vectors", "ax”可以得到,此代码存放在.vectors段里面。

第10行,使用如下命令在uboot中查找“__image_copy_start”:

grep -nR "__image_copy_start"

搜索结果如图32.1.3所示:

图32.1.3 查找结果

打开u-boot.map,找到如图32.1.4所示位置:

图32.1.4 u-boot.map

u-boot.map是uboot的映射文件,可以从此文件看到某个文件或者函数链接到了哪个地址,从图32.1.4的932行可以看到__image_copy_start为0X87800000,而.text的起始地址也是0X87800000。

第11行是vectors段,vectors段保存中断向量表,从图32.1.2中我们知道了vec的代码是存在vectors段中的。从图32.1.4可以看出,vectors段的起始地址也是0X87800000,说明整个uboot的起始地址就是0X87800000,这也是为什么我们裸机例程的链接起始地址选择0X87800000了,目的就是为了和uboot一致。

第12行将arch/arm/cpu/armv7编译出来的代码放到中断向量表后面。

第13行为text段,其他的代码段就放到这里

在u-boot.lds中有一些跟地址有关的“变量”需要我们注意一下,后面分析u-boot源码的时候会用到,这些变量要最终编译完成才能确定的!!!比如我编译完成以后这些“变量”的值如表32.1.1所示:

表32.1.1 uboot相关变量表

表32.1.1中的“变量”值可以在u-boot.map文件中查找,表32.1.1中除了__image_copy_start以外,其他的变量值每次编译的时候可能会变化,如果修改了uboot代码、修改了uboot配置、选用不同的优化等级等等都会影响到这些值。所以,一切以实际值为准!

32.2 U-Boot启动流程详解

32.2.1reset函数源码详解

从u-boot.lds中我们已经知道了入口点是arch/arm/lib文件中的_start,代码如下:

示例代码32.2.1.1 vec代码段

38/*

39 *************************************************************

40 *

41 * Exception vectors as described in ARM reference manuals

42 *

43 * Uses indirect branch to allow reaching handlers anywhere in

44 * memory.

45 **************************************************************

46 */

47

48 _start:

49

50 #ifdef CONFIG_SYS_DV_NOR_BOOT_CFG

51.word CONFIG_SYS_DV_NOR_BOOT_CFG

52 #endif

53

54 b reset

55 ldr pc, _undefined_instruction

56 ldr pc, _software_interrupt

57 ldr pc, _prefetch_abort

58 ldr pc, _data_abort

59 ldr pc, _not_used

60 ldr pc, _irq

61 ldr pc, _fiq

第48行_start开始的是中断向量表,其中54~61行就是中断向量表,和我们裸机例程里面一样。54行跳转到reset函数里面,reset函数在arch/arm/cpu/armv7里面,代码如下:

示例代码32.2.1.2 代码段

22/*****************************************************************

23 *

24 * Startup Code (reset vector)

25 *

26 * Do important init only if we don't start from memory!

27 * Setup memory and board specific bits prior to relocation.

28 * Relocate armboot to ram. Setup stack.

29 *

30 *****************************************************************/

31

32.globl reset

33.globl save_boot_params_ret

34

35 reset:

36 /* Allow the board to save important registers */

37 b save_boot_params

第35行就是reset函数。

第37行从reset函数跳转到了save_boot_params函数,而save_boot_params函数同样定义在里面,定义如下:

示例代码32.2.1.3 代码段

91/******************************************************************

92 *

93 * void save_boot_params(u32 r0, u32 r1, u32 r2, u32 r3)

94 * __attribute__((weak));

95 *

96 * Stack pointer is not yet initialized at this moment

97 * Don't save anything to stack even if compiled with -O0

98 *

99 ******************************************************************/

100 ENTRY(save_boot_params)

101 b save_boot_params_ret @ back to my caller

save_boot_params函数也是只有一句跳转语句,跳转到save_boot_params_ret函数,save_boot_params_ret函数代码如下:

示例代码32.2.1.4 代码段

38 save_boot_params_ret:

39 /*

40 * disable interrupts (FIQ and IRQ), also set the cpu to SVC32

41 * mode, except if in HYP mode already

42 */

43 mrs r0, cpsr

44 and r1, r0, #0x1f @ mask mode bits

45 teq r1, #0x1a @ test for HYP mode

46 bicne r0, r0, #0x1f @ clear all mode bits

47 orrne r0, r0, #0x13 @ set SVC mode

48 orr r0, r0, #0xc0 @ disable FIQ and IRQ

49 msr cpsr,r0

第43行,读取寄存器cpsr中的值,并保存到r0寄存器中。

第44行,将寄存器r0中的值与0X1F进行与运算,结果保存到r1寄存器中,目的就是提取cpsr的bit0~bit4这5位,这5位为M4 M3 M2 M1 M0,M[4:0]这五位用来设置处理器的工作模式,如表32.2.1.1所示:

M[4:0]

模式

10000

User(usr)

10001

FIQ(fiq)

10010

IRQ(irq)

10011

Supervisor(svc)

10110

Monitor(mon)

10111

Abort(abt)

11010

Hyp(hyp)

11011

Undefined(und)

11111

System(sys)

表32.2.1.1 Cortex-A7工作模式

第45行,判断r1寄存器的值是否等于0X1A(0b11010),也就是判断当前处理器模式是否处于Hyp模式。

第46行,如果r1和0X1A不相等,也就是CPU不处于Hyp模式的话就将r0寄存器的bit0~5进行清零,其实就是清除模式位

第47行,如果处理器不处于Hyp模式的话就将r0的寄存器的值与0x13进行或运算,0x13=0b10011,也就是设置处理器进入SVC模式。

第48行,r0寄存器的值再与0xC0进行或运算,那么r0寄存器此时的值就是0xD3,cpsr的I为和F位分别控制IRQ和FIQ这两个中断的开关,设置为1就关闭了FIQ和IRQ!

第49行,将r0寄存器写回到cpsr寄存器中。完成设置CPU处于SVC32模式,并且关闭FIQ和IRQ这两个中断。

继续执行执行下面的代码:

示例代码32.2.1.5 代码段

51 /*

52 * Setup vector:

53 * (OMAP4 spl TEXT_BASE is not 32 byte aligned.

54 * Continue to use ROM code vector only in OMAP4 spl)

55 */

56 #if!(defined(CONFIG_OMAP44XX)&& defined(CONFIG_SPL_BUILD))

57/* Set V=0 in CP15 SCTlr register - for VBAR to point to vector */

58 mrc p15,0, r0, c1, c0,0 @ Read CP15 SCTLR Register

59 bic r0, #CR_V @ V =0

60 mcr p15,0, r0, c1, c0,0 @ Write CP15 SCTLR Register

61

62 /* Set vector address in CP15 VBAR register */

63 ldr r0,=_start

64 mcr p15,0, r0, c12, c0,0 @Set VBAR

65 #endif

第56行,如果没有定义CONFIG_OMAP44XX和CONFIG_SPL_BUILD的话条件成立,此处条件成立。

第58行读取CP15中c1寄存器的值到r0寄存器中,根据17.1.4小节可知,这里是读取SCTLR寄存器的值。

第59行,CR_V在arch/arm/include/asm中有如下所示定义:

#define CR_V (1 << 13) /* Vectors relocated to 0xffff0000 */

因此这一行的目的就是清除SCTLR寄存器中的bit13,SCTLR寄存器结构如图32.2.1.1所示:

图32.2.1.1 SCTLR寄存器结构图

从图32.2.1.1可以看出,bit13为V位,此位是向量表控制位,当为0的时候向量表基地址为0X00000000,软件可以重定位向量表。为1的时候向量表基地址为0XFFFF0000,软件不能重定位向量表。这里将V清零,目的就是为了接下来的向量表重定位,这个我们在第十七章有过详细的介绍了。

第60行将r0寄存器的值重写写入到寄存器SCTLR中。

第63行设置r0寄存器的值为_start,_start就是整个uboot的入口地址,其值为0X87800000,相当于uboot的起始地址,因此0x87800000也是向量表的起始地址。

第64行将r0寄存器的值(向量表值)写入到CP15的c12寄存器中,也就是VBAR寄存器。因此第58~64行就是设置向量表重定位的。

代码继续往下执行:

示例代码32.2.1.6 代码段

67/* the mask ROM code should have PLL and others stable */

68 #ifndef CONFIG_SKIP_LOWLEVEL_INIT

69 bl cpu_init_cp15

70 bl cpu_init_crit

71 #endif

72

73 bl _main

第68行如果没有定义CONFIG_SKIP_LOWLEVEL_INIT的话条件成立。我们没有定义CONFIG_SKIP_LOWLEVEL_INIT,因此条件成立,执行下面的语句。

示例代码32.2.1.6中的内容比较简单,就是分别调用函数cpu_init_cp15、cpu_init_crit和_main。

函数cpu_init_cp15用来设置CP15相关的内容,比如关闭MMU啥的,此函数同样在文件中定义的,代码如下:

示例代码32.2.1.7 代码段

105/*****************************************************************

106 *

107 * cpu_init_cp15

108 *

109 * Setup CP15 registers (cache, MMU, TLBs). The I-cache is turned on

110 * unless CONFIG_SYS_ICACHE_OFF is defined.

111 *

112 *****************************************************************/

113 ENTRY(cpu_init_cp15)

114/*

115 * Invalidate L1 I/D

116 */

117 mov r0, #0 @ set up for MCR

118 mcr p15,0, r0, c8, c7,0 @ invalidate TLBs

119 mcr p15,0, r0, c7, c5,0 @ invalidate icache

120 mcr p15,0, r0, c7, c5,6 @ invalidate BP array

121 mcr p15,0, r0, c7, c10,4 @ DSB

122 mcr p15,0, r0, c7, c5,4 @ ISB

123

124/*

125 * disable MMU stuff and caches

126 */

127 mrc p15,0, r0, c1, c0,0

128 bic r0, r0, #0x00002000 @ clear bits 13(--V-)

129 bic r0, r0, #0x00000007 @ clear bits 2:0(-CAM)

130 orr r0, r0, #0x00000002 @ set bit 1(--A-) Align

131 orr r0, r0, #0x00000800 @ set bit 11(Z---) BTB

132 #ifdef CONFIG_SYS_ICACHE_OFF

133 bic r0, r0, #0x00001000 @ clear bit 12(I) I-cache

134 #else

135 orr r0, r0, #0x00001000 @ set bit 12(I) I-cache

136 #endif

137 mcr p15,0, r0, c1, c0,0

138

......

255

256 mov pc, r5 @ back to my caller

257 ENDPROC(cpu_init_cp15)

函数cpu_init_cp15都是一些和CP15有关的内容,我们不用关心,有兴趣的可以详细的看一下。

函数cpu_init_crit也在是定义在文件中,函数内容如下:

示例代码32.2.1.8 代码段

260/*****************************************************************

261 *

262 * CPU_init_critical registers

263 *

264 * setup important registers

265 * setup memory timing

266 *

267 *****************************************************************/

268 ENTRY(cpu_init_crit)

269/*

270 * Jump to board specific initialization...

271 * The Mask ROM will have already initialized

272 * basic memory. Go here to bump up clock rate and handle

273 * wake up conditions.

274 */

275 b lowlevel_init @ go setup pll,mux,memory

276 ENDPROC(cpu_init_crit)

可以看出函数cpu_init_crit内部仅仅是调用了函数lowlevel_init,接下来就是详细的分析一下lowlevel_init和_main这两个函数。

32.2.2 lowlevel_init函数详解

函数lowlevel_init在文件arch/arm/cpu/armv7中定义,内容如下:

示例代码32.2.2.1 lowlevel_init.S代码段

14 #include <a;

15 #include <con;

16 #include <linux;

17

18 ENTRY(lowlevel_init)

19 /*

20 * Setup a temporary stack. Global data is not available yet.

21 */

22 ldr sp,=CONFIG_SYS_INIT_SP_ADDR

23 bic sp, sp, #7 /* 8-byte alignment for ABI compliance */

24 #ifdef CONFIG_SPL_DM

25 mov r9, #0

26 #else

27 /*

28 * Set up global data for boards that still need it. This will be

29 * removed soon.

30 */

31 #ifdef CONFIG_SPL_BUILD

32 ldr r9,=gdata

33 #else

34 sub sp, sp, #GD_SIZE

35 bic sp, sp, #7

36 mov r9, sp

37 #endif

38 #endif

39 /*

40 * Save the old lr(passed in ip) and the current lr to stack

41 */

42 push {ip, lr}

43

44 /*

45 * Call the very early init function. This should do only the

46 * absolute bare minimum to get started. It should not:

47 *

48 * - set up DRAM

49 * - use global_data

50 * - clear BSS

51 * - try to start a console

52 *

53 * For boards with SPL this should be empty since SPL can do all

54 * of this init in the SPL board_init_f() function which is

55 * called immediately after this.

56 */

57 bl s_init

58 pop {ip, pc}

59 ENDPROC(lowlevel_init)

第22行设置sp指向CONFIG_SYS_INIT_SP_ADDR,CONFIG_SYS_INIT_SP_ADDR在include/config文件中,在mx6ullevk.h中有如下所示定义:

示例代码32.2.2.2 mx6ullevk.h代码段

234 #define CONFIG_SYS_INIT_RAM_ADDR IRAM_BASE_ADDR

235 #define CONFIG_SYS_INIT_RAM_SIZE IRAM_SIZE

236

237 #define CONFIG_SYS_INIT_SP_OFFSET

238(CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE)

239 #define CONFIG_SYS_INIT_SP_ADDR

240(CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_SP_OFFSET)

示例代码32.2.2.2中的IRAM_BASE_ADDR和IRAM_SIZE在文件arch/arm/include/asm/arch-mx6中有定义,如下所示,其实就是IMX6UL/IM6ULL内部ocram的首地址和大小。

示例代码32.2.2.3 imx-regs.h代码段

71 #define IRAM_BASE_ADDR 0x00900000

......

408 #if!(defined(CONFIG_MX6SX)|| defined(CONFIG_MX6UL)||

409 defined(CONFIG_MX6SLL)|| defined(CONFIG_MX6SL))

410 #define IRAM_SIZE 0x00040000

411 #else

412 #define IRAM_SIZE 0x00020000

413 #endif

如果408行的条件成立的话IRAM_SIZE=0X40000,当定义了CONFIG_MX6SX、CONFIG_MX6U、CONFIG_MX6SLL和CONFIG_MX6SL中的任意一个的话条件就不成立,在.config中定义了CONFIG_MX6UL,所以条件不成立,因此IRAM_SIZE=0X20000=128KB。

结合示例代码32.2.2.2,可以得到如下值:

CONFIG_SYS_INIT_RAM_ADDR = IRAM_BASE_ADDR = 0x00900000。

CONFIG_SYS_INIT_RAM_SIZE = 0x00020000 =128KB。

还需要知道GENERATED_GBL_DATA_SIZE的值,在文件include/generated中有定义,如下:

示例代码32.2.2.4 generic-a代码段

1 #ifndef __GENERIC_ASM_OFFSETS_H__

2 #define __GENERIC_ASM_OFFSETS_H__

3/*

4 * DO NOT MODIFY.

5 *

6 * This file was generated by Kbuild

7 */

8

9 #define GENERATED_GBL_DATA_SIZE 256

10 #define GENERATED_BD_INFO_SIZE 80

11 #define GD_SIZE 248

12 #define GD_BD 0

13 #define GD_MALLOC_BASE 192

14 #define GD_RELOCADDR 48

15 #define GD_RELOC_OFF 68

16 #define GD_START_ADDR_SP 64

17

18 #endif

GENERATED_GBL_DATA_SIZE=256,GENERATED_GBL_DATA_SIZE的含义为(sizeof(struct global_data) + 15) & ~15 。

综上所述,CONFIG_SYS_INIT_SP_ADDR值如下:

CONFIG_SYS_INIT_SP_OFFSET = 0x00020000 –256=0x1FF00。

CONFIG_SYS_INIT_SP_ADDR = 0x00900000 +0X1FF00 = 0X0091FF00,

结果如下图所示:

图32.2.2.1 sp值

此时sp指向0X91FF00,这属于IMX6UL/IMX6ULL的内部ram。

继续回到文件lowlevel_init.S,第23行对sp指针做8字节对齐处理!

第34行,sp指针减去GD_SIZE,GD_SIZE同样在generic-a中定了,大小为248,见示例代码32.2.2.4第11行。

第35行对sp做8字节对齐,此时sp的地址为0X0091FF00-248=0X0091FE08,此时sp位置如图32.2.2.2所示:

图32.2.2.2 sp值

第36行将sp地址保存在r9寄存器中。

第42行将ip和lr压栈

第57行调用函数s_init,得,又来了一个函数。

第58行将第36行入栈的ip和lr进行出栈,并将lr赋给pc。

32.2.3 s_init函数详解

在上一小节中,我们知道lowlevel_init函数后面会调用s_init函数,s_init函数定义在文件arch/arm/cpu/armv7/mx6中,如下所示:

示例代码32.2.3.1 代码段

808void s_init(void)

809{

810struct anatop_regs *anatop =(struct anatop_regs *)ANATOP_BASE_ADDR;

811struct mxc_ccm_reg *ccm =(struct mxc_ccm_reg *)CCM_BASE_ADDR;

812 u32 mask480;

813 u32 mask528;

814 u32 reg, periph1, periph2;

815

816if(is_cpu_type(MXC_CPU_MX6SX)|| is_cpu_type(MXC_CPU_MX6UL)||

817 is_cpu_type(MXC_CPU_MX6ULL)|| is_cpu_type(MXC_CPU_MX6SLL))

818return;

819

820/* Due to hardware limitation, on MX6Q we need to gate/ungate

821 * all PFDs to make sure PFD is working right, otherwise, PFDs

822 * may not output clock after reset, MX6DL and MX6SL have added

823 * 396M pfd workaround in ROM code, as bus clock need it

824 */

825

826 mask480 = ANATOP_PFD_CLKGATE_MASK(0)|

827 ANATOP_PFD_CLKGATE_MASK(1)|

828 ANATOP_PFD_CLKGATE_MASK(2)|

829 ANATOP_PFD_CLKGATE_MASK(3);

830 mask528 = ANATOP_PFD_CLKGATE_MASK(1)|

831 ANATOP_PFD_CLKGATE_MASK(3);

832

833 reg = readl(&ccm->cbcmr);

834 periph2 =((reg & MXC_CCM_CBCMR_PRE_PERIPH2_CLK_SEL_MASK)

835>> MXC_CCM_CBCMR_PRE_PERIPH2_CLK_SEL_OFFSET);

836 periph1 =((reg & MXC_CCM_CBCMR_PRE_PERIPH_CLK_SEL_MASK)

837>> MXC_CCM_CBCMR_PRE_PERIPH_CLK_SEL_OFFSET);

838

839/* Checking if PLL2 PFD0 or PLL2 PFD2 is using for periph clock */

840if((periph2 !=0x2)&&(periph1 !=0x2))

841 mask528 |= ANATOP_PFD_CLKGATE_MASK(0);

842

843if((periph2 !=0x1)&&(periph1 !=0x1)&&

844(periph2 !=0x3)&&(periph1 !=0x3))

845 mask528 |= ANATOP_PFD_CLKGATE_MASK(2);

846

847 writel(mask480,&anatop->pfd_480_set);

848 writel(mask528,&anatop->pfd_528_set);

849 writel(mask480,&anatop->pfd_480_clr);

850 writel(mask528,&anatop->pfd_528_clr);

851}

在第816行会判断当前CPU类型,如果CPU为MX6SX、MX6UL、MX6ULL或MX6SLL中的任意一种,那么就会直接返回,相当于s_init函数什么都没做。所以对于I.MX6UL来说,s_init就是个空函数。从s_init函数退出以后进入函数lowlevel_init,但是lowlevel_init函数也执行完成了,返回到了函数cpu_init_crit,函数cpu_init_crit也执行完成了,最终返回到save_boot_params_ret,函数调用路径如图32.2.3.1所示:

图32.2.3.1 uboot函数调用路径

从图32.2.3.1可知,接下来要执行的是save_boot_params_ret中的_main函数,接下来分析_main函数。

1.《关于00000007b怎么解决,你需要知道这些「正点原子Linux连载」第三十二章U-Boot启动流程详解(一)》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。

2.《关于00000007b怎么解决,你需要知道这些「正点原子Linux连载」第三十二章U-Boot启动流程详解(一)》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。

3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/gl/2245842.html

上一篇

让超薄设计也能兼顾体验,OPPO R7 & R7 Plus 发布

下一篇

09帕克如何使用?终于找到答案了盘点拉菲古堡的好年份,终于知道为什么要来瓶82年的拉菲了!

00000007b怎么解决?我来告诉你答案深入JVM 原理(五)Java堆内存调整参数(调优关键)

00000007b怎么解决?我来告诉你答案深入JVM 原理(五)Java堆内存调整参数(调优关键)

00000007b怎么解决相关介绍,调整堆内存参数 以前的分析表明,实际上每个子内存区都有一部分可变区。基本过程如下: 如果空间不足,则在可变范围内扩展内存空间,如果在一段时间后发现内存空间没有这么紧张,则释放可变空间。 所以在整个调整...

00000007b怎么解决?终于找到答案了深入JVM 原理(五)Java堆内存调整参数(调优关键)

00000007b怎么解决?终于找到答案了深入JVM 原理(五)Java堆内存调整参数(调优关键)

00000007b怎么解决相关介绍,堆内存的参数调整 通过之前的分析可以发现,实际上每一块子内存区中都会存在有一部分的可变伸缩区,其基本流程: 如果空间不足,在可变的范围之内扩大内存空间,当一段时间之后发现内存空间没有这么紧张的时候,再...

00000007b怎么解决看这里!深入JVM 原理(五)Java堆内存调整参数(调优关键)

00000007b怎么解决看这里!深入JVM 原理(五)Java堆内存调整参数(调优关键)

00000007b怎么解决相关介绍,堆内存的参数调整 通过之前的分析可以发现,实际上每一块子内存区中都会存在有一部分的可变伸缩区,其基本流程: 如果空间不足,在可变的范围之内扩大内存空间,当一段时间之后发现内存空间没有这么紧张的时候,再...

00000007b怎么解决?总结很全面速看!深入JVM 原理(五)Java堆内存调整参数(调优关键)

00000007b怎么解决?总结很全面速看!深入JVM 原理(五)Java堆内存调整参数(调优关键)

00000007b怎么解决相关介绍,调整堆内存参数 以前的分析表明,实际上每个子内存区都有一部分可变区。基本过程如下: 如果空间不足,则在可变范围内扩展内存空间,如果在一段时间后发现内存空间没有这么紧张,则释放可变空间。 所以在整个调整...

00000007b怎么解决?总结很全面速看!宁波上元教育:什么是并发编程,并发编程的优缺点

00000007b怎么解决?总结很全面速看!宁波上元教育:什么是并发编程,并发编程的优缺点

00000007b怎么解决相关介绍,一直以来,并发编程对新进来的小白来说一直觉得很深刻,所以诞生了,为了提高理解和积累并发编程的认识,想写点什么。(莎士比亚,莎士比亚。) 为什么需要用的并发? 凡事总有好坏两面,之间的trade-off...

00000007b怎么解决看这里!JVM系列(七)运行时数据区(方法区)

00000007b怎么解决看这里!JVM系列(七)运行时数据区(方法区)

00000007b怎么解决相关介绍,1.堆栈、堆栈、方法区域交互 查看是否共享线程。 互动 2.理解方法领域 2.1正式文件 the Java virtual machine has a method area that is shar...

关于00000007b怎么解决,你需要知道这些宁波上元教育:什么是并发编程,并发编程的优缺点

关于00000007b怎么解决,你需要知道这些宁波上元教育:什么是并发编程,并发编程的优缺点

00000007b怎么解决相关介绍,一直以来并发编程对于刚入行的小白来说总是觉得高深莫测,于是乎,就诞生了想写点东西记录下,以提升理解和堆并发编程的认知。为什么需要用的并发? 凡事总有好坏两面,之间的trade-off是什么,也就是说并...

00000007b怎么解决?我来告诉你答案JVM系列(七)运行时数据区(方法区)

00000007b怎么解决?我来告诉你答案JVM系列(七)运行时数据区(方法区)

00000007b怎么解决相关介绍,1. 栈、堆、方法区的交互关系 从线程共享与否的角度来看 交互关系 2. 方法区的理解 2.1 官方文档 The Java Virtual Machine has a method area that...