主頁 > 資料庫 > 在實際CPU上,未初始化變數的值從何而來?

在實際CPU上,未初始化變數的值從何而來?

2022-03-04 15:40:57 資料庫

我想知道變數的初始化方式:

#include <stdio.h>
int main( void )
{
    int ghosts[3];
    for(int i =0 ; i < 3 ; i  )
    printf("%d\n",ghosts[i]);
    return 0;
}

這讓我得到隨機值,例如 -12 2631 131 .. 它們來自哪里?

例如 x86-64 Linux 上的 GCC:https ://godbolt.org/z/MooEE3ncc

我有一個猜測來回答我的問題,無論如何它可能是錯誤
的:存盤器的暫存器在“清空”后會獲得 0 到 1 之間的隨機電壓,這些值會“四舍五入”為 0 或 1,這些隨機值取決于在某事上?!也許暫存器的制作方式?也許記憶體的容量以某種方式起作用?甚至可能是溫度?!

uj5u.com熱心網友回復:

被“清空”后的記憶體暫存器會獲得 0 到 1 之間的隨機電壓,

沒有什么比這更神秘的了。您只是看到上次使用這些記憶體位置時寫入的內容。

當記憶體被釋放時,它不會被清除或清空。系統只知道它是免費的,下次有人需要記憶體時,它就會被移交,舊的內容仍然存在。就像買了一輛舊車,在雜物箱里看,里面的東西并不神秘,找到一個點煙器和一只襪子只是一個驚喜。

有時在除錯環境中釋放的記憶體會被清除為某個可識別的值,以便很容易識別出您正在處理未初始化的記憶體。例如 0xccccccccccc 或者 0xdeadbeefDeadBeef

也許是一個更好的比喻。你在一家從不清洗盤子的自助餐廳吃飯,當顧客吃完后,他們把盤子放回“免費”堆上。當你去為自己服務時,你會從空閑堆中拿起頂板。您應該清潔盤子,否則您會得到以前客戶留下的東西

uj5u.com熱心網友回復:

每次運行新程式時,您的計算機都不會重新啟動或重新啟動。你的程式可以使用的記憶體或暫存器中的每一位存盤都有一個先前的指令留下的值,無論是在這個程式中還是在啟動這個程式之前的作業系統中。

如果是這種情況,例如對于微控制器,是的,存盤的每個位都可能在通電的電壓波動期間穩定為 0 或 1 狀態,除非存盤設計為在特定狀態下通電。(DRAM 上電時更可能為 0,因為它的電容器已經放電)。但是您還希望有內部 CPU 邏輯在從復位向量(記憶體地址)獲取和執行代碼的第一條指令之前執行一些歸零或設定為保證狀態;系統設計人員通常會在該物理地址上安排 ROM,而不是 RAM,因此他們可以在那里放置機器代碼的非隨機位元組。在該地址執行的代碼可能應該為所有暫存器假定隨機值。

但是您正在撰寫一個在作業系統下運行的簡單用戶空間程式,而不是在微控制器、嵌入式系統或主流主板的韌體下運行,因此當任何東西加載您的程式時,上電隨機性早已成為過去。


現代作業系統在行程啟動時將暫存器歸零,并將分配給用戶空間(包括您的堆疊空間)的記憶體頁面歸零,以避免內核資料和來自其他行程的資料的資訊泄漏。因此,這些值必須來自行程中較早發生的事情,可能來自之前運行main并使用了一些堆疊空間的動態聯結器代碼。

讀取從未初始化或分配的區域變數的值實際上并不是未定義的行為(在這種情況下,因為它不能被宣告register int ghosts[3],這是一個錯誤(Godbolt),因為ghosts[i]有效地使用了地址)參見 (為什么)正在使用未初始化的變數未定義的行為? 在這種情況下,所有 C 標準必須說的是該值是不確定的。 因此,正如您所料,它確實歸結為實施細節。

當您在沒有優化的情況下進行編譯時,編譯器甚至不會注意到 UB,因為它們不會跟蹤 C 陳述句的使用情況(這意味著一切都被視為有點像volatile,只根據陳述句的需要將值加載到暫存器中,然后再次存盤。)

在我添加到您的問題的示例 Godbolt 鏈接中,請注意-Wall不會在 處產生任何警告-O0,并且只是從它為陣列選擇的堆疊記憶體中讀取,而無需寫入它。 因此,您的代碼正在觀察函式啟動時記憶體中的任何陳舊值。 (但正如我所說,這一定是在這個程式的早期撰寫的,通過 C 啟動代碼或動態鏈接。)

使用gcc -O2 -Wall,我們得到了我們期望的警告:warning: 'ghosts' is used uninitialized [-Wuninitialized],但它仍然從堆疊空間讀取而不寫入它。

有時 GCC 會發明一個0而不是讀取未初始化的堆疊空間,但在這種情況下不會發生。關于它如何編譯編譯器看到使用未初始化的“錯誤”并且可以發明它想要的任何值,例如讀取一些它從未寫過的暫存器而不是那個記憶體,這是零保證。例如,由于您正在呼叫 printf,GCC 可能只是在printf呼叫之間未初始化 ESI,因為這ghost[i]是在 x86-64 System V 呼叫約定中作為第二個引數傳遞的地方。


Most modern CPUs including x86 don't have any "trap representations" that would make an add instruction fault, and even if it did the C standard doesn't guarantee that the indeterminate value isn't a trap representation. But IA-64 did have a Not A Thing register result from bad speculative loads, which would trap if you tried to read it. See comments on the trap representation Q&A - Raymond Chen's article: Uninitialized garbage on ia64 can be deadly.

The ISO C rule about it being UB to read uninitialized variables that were candidates for register might be aimed at this, but with optimization enabled you could plausibly still run into this anyway if the taking of the address happens later, unless the compiler takes steps to avoid it. But ISO C defect report N1208 proposes saying that an indeterminate value can be "a value that behaves as if it were a trap representation" even for types that have no trap representations. So it seems that part of the standard doesn't fully cover ISAs like IA-64, the way real compilers can work.

Another case that's not exactly a "trap representation": note that only some object-representations (bit patterns) are valid for _Bool in mainstream ABIs, and violating that can crash your program: Does the C standard allow for an uninitialized bool to crash a program?

That's a C question, but I verified that GCC will return garbage without booleanizing it to 0/1 if you write _Bool b[2] ; return b[0]; https://godbolt.org/z/jMr98547o. I think ISO C only requires that an uninitialized object has some object-representation (bit-pattern), not that it's a valid one for this object (otherwise that would be a compiler bug). For most integer types, every bit-pattern is valid and represents an integer value. Besides reading uninitialized memory, you can cause the same problem using (unsigned char*) or memcpy to write a bad byte into a _Bool.

uj5u.com熱心網友回復:

我將使用一個易于查看正在發生的事情的平臺。編譯器和平臺的作業方式相同,獨立于架構、作業系統等。當然也有例外......

在 main 我要呼叫這個函式

test();

哪個是

extern void hexstring ( unsigned int );

void test ( void )
{
    unsigned int x[3];
    hexstring(x[0]);
    hexstring(x[1]);
    hexstring(x[2]);
}

hexstring 只是一個 printf("8X\n",x);

Build it (not using x86, using something that is overall easier to read for this demonstration)

test.c: In function ‘test’:
test.c:7:2: warning: ‘x[0]’ is used uninitialized in this function [-Wuninitialized]
    7 |  hexstring(x[0]);
      |  ^~~~~~~~~~~~~~~
test.c:8:2: warning: ‘x[1]’ is used uninitialized in this function [-Wuninitialized]
    8 |  hexstring(x[1]);
      |  ^~~~~~~~~~~~~~~
test.c:9:2: warning: ‘x[2]’ is used uninitialized in this function [-Wuninitialized]
    9 |  hexstring(x[2]);
      |  ^~~~~~~~~~~~~~~

編譯器輸出的反匯編顯示

00010134 <test>:
   10134:   e52de004    push    {lr}        ; (str lr, [sp, #-4]!)
   10138:   e24dd014    sub sp, sp, #20
   1013c:   e59d0004    ldr r0, [sp, #4]
   10140:   ebffffdc    bl  100b8 <hexstring>
   10144:   e59d0008    ldr r0, [sp, #8]
   10148:   ebffffda    bl  100b8 <hexstring>
   1014c:   e59d000c    ldr r0, [sp, #12]
   10150:   e28dd014    add sp, sp, #20
   10154:   e49de004    pop {lr}        ; (ldr lr, [sp], #4)
   10158:   eaffffd6    b   100b8 <hexstring>

我們可以看到堆疊區被分配了

   10138:   e24dd014    sub sp, sp, #20

但是然后我們直接進入閱讀和列印

   1013c:   e59d0004    ldr r0, [sp, #4]
   10140:   ebffffdc    bl  100b8 <hexstring>

所以無論是在堆疊上。堆疊只是帶有特殊硬體指標的記憶體。

我們可以看到陣列中的其他兩項也被讀取(加載)和列印。

因此,此時記憶中的任何內容都會被列印出來。現在,在我們到達那里之前,我所處的環境可能會將記憶體(包括堆疊)歸零:

00000000 
00000000 
00000000 

現在我正在優化此代碼以使其更易于閱讀,這增加了一些挑戰。

So what if we did this:

test2();
test();

In main and

void test2 ( void )
{
    unsigned int y[3];
    y[0]=1;
    y[1]=2;
    y[2]=3;
}

test2.c: In function ‘test2’:
test2.c:5:15: warning: variable ‘y’ set but not used [-Wunused-but-set-variable]
    5 |  unsigned int y[3];
      |  

and we get

00000000 
00000000 
00000000 

but we can see why:

00010124 <test>:
   10124:   e52de004    push    {lr}        ; (str lr, [sp, #-4]!)
   10128:   e24dd014    sub sp, sp, #20
   1012c:   e59d0004    ldr r0, [sp, #4]
   10130:   ebffffe0    bl  100b8 <hexstring>
   10134:   e59d0008    ldr r0, [sp, #8]
   10138:   ebffffde    bl  100b8 <hexstring>
   1013c:   e59d000c    ldr r0, [sp, #12]
   10140:   e28dd014    add sp, sp, #20
   10144:   e49de004    pop {lr}        ; (ldr lr, [sp], #4)
   10148:   eaffffda    b   100b8 <hexstring>

0001014c <test2>:
   1014c:   e12fff1e    bx  lr

test didnt change but test2 is dead code as one would expect when optimized, so it did not actually touch the stack. But what if we:

test2.c

void test3 ( unsigned int * );

void test2 ( void )
{
    unsigned int y[3];
    y[0]=1;
    y[1]=2;
    y[2]=3;
    test3(y);
}

test3.c

void test3 ( unsigned int *x )
{
}

Now

0001014c <test2>:
   1014c:   e3a01001    mov r1, #1
   10150:   e3a02002    mov r2, #2
   10154:   e3a03003    mov r3, #3
   10158:   e52de004    push    {lr}        ; (str lr, [sp, #-4]!)
   1015c:   e24dd014    sub sp, sp, #20
   10160:   e28d0004    add r0, sp, #4
   10164:   e98d000e    stmib   sp, {r1, r2, r3}
   10168:   eb000001    bl  10174 <test3>
   1016c:   e28dd014    add sp, sp, #20
   10170:   e49df004    pop {pc}        ; (ldr pc, [sp], #4)

00010174 <test3>:
   10174:   e12fff1e    bx  lr

test2 is actually putting stuff on the stack. Now the calling conventions generally require that the stack pointer is back where it started when you were called, which means function a might move the pointer and read/write some data in that space, call function b move the pointer, read/write some data in that space, and so on. Then when each function returns it does not make sense usually to clean up, you just move the pointer back and return whatever data you wrote to that memory remains.

So if test 2 writes a few things to the stack memory space and then returns then another function is called at the same level as test2. Then the stack pointer is at the same address when test() is called as when test2() was called, in this example. So what happens?

00000001 
00000002 
00000003 

We have managed to control what test() is printing out. Not magic.

Now rewind back to the 1960s and then work forward to the present, particularly 1980s and later.

Memory was not always cleaned up before your program ran. As some folks here are implying if you were doing banking on a spreadsheet then you closed that program and opened this program...back in the day...you would almost expect to see some data from that spreadsheet program, maybe the binary maybe the data, maybe something else, due to the nature of the operating systems use of memory it may be a fragment of the last program you ran, and a fragment of the one before that, and a fragment of a program still running that just did a free(), and so on.

Naturally, once we started to get connected to each other and hackers wanted to take over and send themselves your info or do other bad things, you can see how trivial it would be to write a program to look for passwords or bank accounts or whatever.

So not only do we have protections today to prevent one program sniffing around in another programs space, we generally assume that, today, before our program gets some memory that was used by some other program, it is wiped.

But if you disassemble even a simple hello world printf program you will see that there is a fair amount of bootstrap code that happens before main() is called. As far as the operating system is concerned, all of that code is part of our one program so even if (lets assume) memory were zeroed or cleaned before the OS loads and launches our program. Before main, within our program, we are using the stack memory to do stuff, leaving behind values, that a function like test() will see.

You may find that each time you run the same binary, one compile many runs, that the "random" data is the same. Now you may find that if you add some other shared library call or something to the overall program, then maybe, maybe, that shared library stuff causes extra code pre-main to happen to try to be able to call the shared code, or maybe as the program runs it takes different paths now because of a side effect of a change to the overall binary and now the random values are different but consistent.

There are explanations why the values could be different each time from the same binary as well.

There is no ghost in the machine though. Stack is just memory, not uncommon when a computer boots to wipe that memory once if for no other reason than to set the ecc bits. After that that memory gets reused and reused and reused and reused. And depending on the overall architecture of the operating system. How the compiler builds your application and shared libraries. And other factors. What happens to be in memory where the stack pointer is pointing when your program runs and you read before you write (as a rule never read before you write, and good that compilers are now throwing warnings) is not necessarily random and the specific list of events that happened to get to that point, were not just random but controlled, are not values that you as the programmer may have predicted. Particularly if you do this at the main() level as you have. But be it main or seventeen levels of nested function calls, it is still just some memory that may or may not contain some stuff from before you got there. Even if the bootloader zeros memory, that is still a written zero that was left behind from some other program that came before you.

毫無疑問,編譯器具有與堆疊相關的功能,這些功能可能會在呼叫結束時執行更多作業,例如在呼叫結束時歸零或預先歸零或出于安全性或某人想到的其他原因。

我今天假設,當像 windows 或 linux 或 macos 這樣的作業系統運行您的程式時,它不會讓您訪問之前出現的其他程式(包含我的銀行資訊、電子郵件、密碼等的電子表格)的一些陳舊記憶體值。但是您可以簡單地撰寫一個程式來嘗試(只需 malloc() 并列印或執行與您所做的相同的事情,但要查看堆疊更大)。我還假設程式 A 沒有辦法進入同時運行的程式 B 的記憶體。至少不是在應用程式級別。沒有黑客攻擊(malloc() 和 print 在我使用該術語時不是黑客攻擊)。

uj5u.com熱心網友回復:

該陣列ghosts未初始化,并且因為它是在函式內部宣告的并且不是static(正式地,它具有自動存盤持續時間),所以它的值是不確定的。

這意味著您可以讀取任何值,并且不能保證任何特定值。

轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/437333.html

標籤:C 集会 cpu架构

上一篇:如何在Python中使用Unicorn正確模擬x86?

下一篇:在沒有分支操作碼的情況下測驗一個整數是否不同于eBPF中的其他兩個整數

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • GPU虛擬機創建時間深度優化

    **?桔妹導讀:**GPU虛擬機實體創建速度慢是公有云面臨的普遍問題,由于通常情況下創建虛擬機屬于低頻操作而未引起業界的重視,實際生產中還是存在對GPU實體創建時間有苛刻要求的業務場景。本文將介紹滴滴云在解決該問題時的思路、方法、并展示最終的優化成果。 從公有云服務商那里購買過虛擬主機的資深用戶,一 ......

    uj5u.com 2020-09-10 06:09:13 more
  • 可編程網卡芯片在滴滴云網路的應用實踐

    **?桔妹導讀:**隨著云規模不斷擴大以及業務層面對延遲、帶寬的要求越來越高,采用DPDK 加速網路報文處理的方式在橫向縱向擴展都出現了局限性。可編程芯片成為業界熱點。本文主要講述了可編程網卡芯片在滴滴云網路中的應用實踐,遇到的問題、帶來的收益以及開源社區貢獻。 #1. 資料中心面臨的問題 隨著滴滴 ......

    uj5u.com 2020-09-10 06:10:21 more
  • 滴滴資料通道服務演進之路

    **?桔妹導讀:**滴滴資料通道引擎承載著全公司的資料同步,為下游實時和離線場景提供了必不可少的源資料。隨著任務量的不斷增加,資料通道的整體架構也隨之發生改變。本文介紹了滴滴資料通道的發展歷程,遇到的問題以及今后的規劃。 #1. 背景 資料,對于任何一家互聯網公司來說都是非常重要的資產,公司的大資料 ......

    uj5u.com 2020-09-10 06:11:05 more
  • 滴滴AI Labs斬獲國際機器翻譯大賽中譯英方向世界第三

    **桔妹導讀:**深耕人工智能領域,致力于探索AI讓出行更美好的滴滴AI Labs再次斬獲國際大獎,這次獲獎的專案是什么呢?一起來看看詳細報道吧! 近日,由國際計算語言學協會ACL(The Association for Computational Linguistics)舉辦的世界最具影響力的機器 ......

    uj5u.com 2020-09-10 06:11:29 more
  • MPP (Massively Parallel Processing)大規模并行處理

    1、什么是mpp? MPP (Massively Parallel Processing),即大規模并行處理,在資料庫非共享集群中,每個節點都有獨立的磁盤存盤系統和記憶體系統,業務資料根據資料庫模型和應用特點劃分到各個節點上,每臺資料節點通過專用網路或者商業通用網路互相連接,彼此協同計算,作為整體提供 ......

    uj5u.com 2020-09-10 06:11:41 more
  • 滴滴資料倉庫指標體系建設實踐

    **桔妹導讀:**指標體系是什么?如何使用OSM模型和AARRR模型搭建指標體系?如何統一流程、規范化、工具化管理指標體系?本文會對建設的方法論結合滴滴資料指標體系建設實踐進行解答分析。 #1. 什么是指標體系 ##1.1 指標體系定義 指標體系是將零散單點的具有相互聯系的指標,系統化的組織起來,通 ......

    uj5u.com 2020-09-10 06:12:52 more
  • 單表千萬行資料庫 LIKE 搜索優化手記

    我們經常在資料庫中使用 LIKE 運算子來完成對資料的模糊搜索,LIKE 運算子用于在 WHERE 子句中搜索列中的指定模式。 如果需要查找客戶表中所有姓氏是“張”的資料,可以使用下面的 SQL 陳述句: SELECT * FROM Customer WHERE Name LIKE '張%' 如果需要 ......

    uj5u.com 2020-09-10 06:13:25 more
  • 滴滴Ceph分布式存盤系統優化之鎖優化

    **桔妹導讀:**Ceph是國際知名的開源分布式存盤系統,在工業界和學術界都有著重要的影響。Ceph的架構和演算法設計發表在國際系統領域頂級會議OSDI、SOSP、SC等上。Ceph社區得到Red Hat、SUSE、Intel等大公司的大力支持。Ceph是國際云計算領域應用最廣泛的開源分布式存盤系統, ......

    uj5u.com 2020-09-10 06:14:51 more
  • es~通過ElasticsearchTemplate進行聚合~嵌套聚合

    之前寫過《es~通過ElasticsearchTemplate進行聚合操作》的文章,這一次主要寫一個嵌套的聚合,例如先對sex集合,再對desc聚合,最后再對age求和,共三層嵌套。 Aggregations的部分特性類似于SQL語言中的group by,avg,sum等函式,Aggregation ......

    uj5u.com 2020-09-10 06:14:59 more
  • 爬蟲日志監控 -- Elastc Stack(ELK)部署

    傻瓜式部署,只需替換IP與用戶 導讀: 現ELK四大組件分別為:Elasticsearch(核心)、logstash(處理)、filebeat(采集)、kibana(可視化) 下載均在https://www.elastic.co/cn/downloads/下tar包,各組件版本最好一致,配合fdm會 ......

    uj5u.com 2020-09-10 06:15:05 more
最新发布
  • day02-2-商鋪查詢快取

    功能02-商鋪查詢快取 3.商鋪詳情快取查詢 3.1什么是快取? 快取就是資料交換的緩沖區(稱作Cache),是存盤資料的臨時地方,一般讀寫性能較高。 快取的作用: 降低后端負載 提高讀寫效率,降低回應時間 快取的成本: 資料一致性成本 代碼維護成本 運維成本 3.2需求說明 如下,當我們點擊商店詳 ......

    uj5u.com 2023-04-20 08:33:24 more
  • MySQL中binlog備份腳本分享

    關于MySQL的二進制日志(binlog),我們都知道二進制日志(binlog)非常重要,尤其當你需要point to point災難恢復的時侯,所以我們要對其進行備份。關于二進制日志(binlog)的備份,可以基于flush logs方式先切換binlog,然后拷貝&壓縮到到遠程服務器或本地服務器 ......

    uj5u.com 2023-04-20 08:28:06 more
  • day02-短信登錄

    功能實作02 2.功能01-短信登錄 2.1基于Session實作登錄 2.1.1思路分析 2.1.2代碼實作 2.1.2.1發送短信驗證碼 發送短信驗證碼: 發送驗證碼的介面為:http://127.0.0.1:8080/api/user/code?phone=xxxxx<手機號> 請求方式:PO ......

    uj5u.com 2023-04-20 08:27:27 more
  • 快取與資料庫雙寫一致性幾種策略分析

    本文將對幾種快取與資料庫保證資料一致性的使用方式進行分析。為保證高并發性能,以下分析場景不考慮執行的原子性及加鎖等強一致性要求的場景,僅追求最終一致性。 ......

    uj5u.com 2023-04-20 08:26:48 more
  • sql陳述句優化

    問題查找及措施 問題查找 需要找到具體的代碼,對其進行一對一優化,而非一直把關注點放在服務器和sql平臺 降低簡化每個事務中處理的問題,盡量不要讓一個事務拖太長的時間 例如檔案上傳時,應將檔案上傳這一步放在事務外面 微軟建議 4.啟動sql定時執行計劃 怎么啟動sqlserver代理服務-百度經驗 ......

    uj5u.com 2023-04-20 08:26:35 more
  • 云時代,MySQL到ClickHouse資料同步產品對比推薦

    ClickHouse 在執行分析查詢時的速度優勢很好的彌補了MySQL的不足,但是對于很多開發者和DBA來說,如何將MySQL穩定、高效、簡單的同步到 ClickHouse 卻很困難。本文對比了 NineData、MaterializeMySQL(ClickHouse自帶)、Bifrost 三款產品... ......

    uj5u.com 2023-04-20 08:26:29 more
  • sql陳述句優化

    問題查找及措施 問題查找 需要找到具體的代碼,對其進行一對一優化,而非一直把關注點放在服務器和sql平臺 降低簡化每個事務中處理的問題,盡量不要讓一個事務拖太長的時間 例如檔案上傳時,應將檔案上傳這一步放在事務外面 微軟建議 4.啟動sql定時執行計劃 怎么啟動sqlserver代理服務-百度經驗 ......

    uj5u.com 2023-04-20 08:25:13 more
  • Redis 報”OutOfDirectMemoryError“(堆外記憶體溢位)

    Redis 報錯“OutOfDirectMemoryError(堆外記憶體溢位) ”問題如下: 一、報錯資訊: 使用 Redis 的業務介面 ,產生 OutOfDirectMemoryError(堆外記憶體溢位),如圖: 格式化后的報錯資訊: { "timestamp": "2023-04-17 22: ......

    uj5u.com 2023-04-20 08:24:54 more
  • day02-2-商鋪查詢快取

    功能02-商鋪查詢快取 3.商鋪詳情快取查詢 3.1什么是快取? 快取就是資料交換的緩沖區(稱作Cache),是存盤資料的臨時地方,一般讀寫性能較高。 快取的作用: 降低后端負載 提高讀寫效率,降低回應時間 快取的成本: 資料一致性成本 代碼維護成本 運維成本 3.2需求說明 如下,當我們點擊商店詳 ......

    uj5u.com 2023-04-20 08:24:03 more
  • day02-短信登錄

    功能實作02 2.功能01-短信登錄 2.1基于Session實作登錄 2.1.1思路分析 2.1.2代碼實作 2.1.2.1發送短信驗證碼 發送短信驗證碼: 發送驗證碼的介面為:http://127.0.0.1:8080/api/user/code?phone=xxxxx<手機號> 請求方式:PO ......

    uj5u.com 2023-04-20 08:23:11 more