[{"data":1,"prerenderedAt":3315},["ShallowReactive",2],{"content:\u002F04-advanced\u002F03-memory-allocator":3},{"title":4,"description":5,"path":6,"body":7},"Аллокатор памяти","","\u002F04-advanced\u002F03-memory-allocator",{"type":8,"value":9,"toc":3291},"minimark",[10,28,116,122,407,417,420,441,443,446,454,459,470,475,478,481,483,497,499,502,505,510,513,516,519,521,580,582,585,588,593,598,612,616,622,664,670,718,725,729,788,887,897,901,912,916,1022,1024,1042,1044,1047,1103,1167,1259,1266,1342,1345,1370,1373,1399,1402,1452,1460,1515,1518,1565,1568,1570,1590,1592,1597,1600,1605,1608,1610,1619,1625,1632,1635,1641,1644,1647,1653,1656,1662,1664,1690,1692,1697,1700,1705,1711,1717,1723,1726,1728,1746,1752,1754,1758,1762,1765,1771,1774,1779,1852,1856,1861,1962,1967,1971,2068,2071,2077,2081,2086,2152,2154,2176,2178,2181,2186,2192,2198,2205,2211,2216,2219,2222,2225,2230,2241,2246,2286,2289,2295,2300,2303,2306,2308,2338,2340,2344,2348,2441,2444,2447,2450,2454,2575,2579,2723,2726,2730,2828,2831,2834,2838,2845,2847,2867,2869,2874,2877,2880,2883,2888,2891,2894,2899,2902,2904,2908,2951,3072,3287],[11,12,13,22,25],"ul",{},[14,15,16,17,21],"li",{},"разница ",[18,19,20],"strong",{},"не аппаратная"," (одна планка RAM), а программная: абстракции ОС",[14,23,24],{},"стек быстрее: простой алгоритм, кэш-дружелюбность, нет фрагментации, нет GC, нет syscall'ов",[14,26,27],{},"компилятор кладёт на стек всё что может, на кучу — только если обязан (escape analysis)",[29,30,31,46],"table",{},[32,33,34],"thead",{},[35,36,37,40,43],"tr",{},[38,39],"th",{},[38,41,42],{},"Стек",[38,44,45],{},"Куча",[47,48,49,61,72,83,94,105],"tbody",{},[35,50,51,55,58],{},[52,53,54],"td",{},"скорость аллокации",[52,56,57],{},"быстро (сдвиг указателя)",[52,59,60],{},"медленно (поиск блока)",[35,62,63,66,69],{},[52,64,65],{},"освобождение",[52,67,68],{},"автоматически при return",[52,70,71],{},"GC",[35,73,74,77,80],{},[52,75,76],{},"время жизни",[52,78,79],{},"пока функция выполняется",[52,81,82],{},"пока есть ссылки",[35,84,85,88,91],{},[52,86,87],{},"размер",[52,89,90],{},"2KB -> до ~1GB",[52,92,93],{},"ограничен RAM",[35,95,96,99,102],{},[52,97,98],{},"доступ",[52,100,101],{},"только своя горутина",[52,103,104],{},"любая горутина",[35,106,107,110,113],{},[52,108,109],{},"синхронизация",[52,111,112],{},"не нужна",[52,114,115],{},"нужна (локи в аллокаторе)",[117,118,119],"p",{},[18,120,121],{},"Когда что:",[123,124,129],"pre",{"className":125,"code":126,"language":127,"meta":128,"style":5},"language-go shiki shiki-themes github-dark","func stack() {\n    x := 42              \u002F\u002F стек: локальная, не escape\n    arr := [100]int{}    \u002F\u002F стек: фиксированный размер, не escape\n    _ = x; _ = arr\n}\n\nfunc heap() *int {\n    x := 42\n    return &x            \u002F\u002F куча: указатель уходит наверх\n}\n\nfunc alsoHeap() {\n    n := 100\n    x := 42\n    s := make([]int, n)  \u002F\u002F может быть стек или куча: зависит от использования\n\n    var i interface{} = 42  \u002F\u002F сам boxing не обязан уходить в кучу\n\n    go func() {\n        println(x)       \u002F\u002F куча: x захвачен closure\n    }()\n    _ = s; _ = i\n}\n","go","static",[130,131,132,149,166,192,209,215,222,239,249,264,269,274,284,295,304,326,331,353,358,369,381,387,402],"code",{"__ignoreMap":5},[133,134,137,141,145],"span",{"class":135,"line":136},"line",1,[133,138,140],{"class":139},"snl16","func",[133,142,144],{"class":143},"svObZ"," stack",[133,146,148],{"class":147},"s95oV","() {\n",[133,150,152,155,158,162],{"class":135,"line":151},2,[133,153,154],{"class":147},"    x ",[133,156,157],{"class":139},":=",[133,159,161],{"class":160},"sDLfK"," 42",[133,163,165],{"class":164},"sAwPA","              \u002F\u002F стек: локальная, не escape\n",[133,167,169,172,174,177,180,183,186,189],{"class":135,"line":168},3,[133,170,171],{"class":147},"    arr ",[133,173,157],{"class":139},[133,175,176],{"class":147}," [",[133,178,179],{"class":160},"100",[133,181,182],{"class":147},"]",[133,184,185],{"class":139},"int",[133,187,188],{"class":147},"{}    ",[133,190,191],{"class":164},"\u002F\u002F стек: фиксированный размер, не escape\n",[133,193,195,198,201,204,206],{"class":135,"line":194},4,[133,196,197],{"class":147},"    _ ",[133,199,200],{"class":139},"=",[133,202,203],{"class":147}," x; _ ",[133,205,200],{"class":139},[133,207,208],{"class":147}," arr\n",[133,210,212],{"class":135,"line":211},5,[133,213,214],{"class":147},"}\n",[133,216,218],{"class":135,"line":217},6,[133,219,221],{"emptyLinePlaceholder":220},true,"\n",[133,223,225,227,230,233,236],{"class":135,"line":224},7,[133,226,140],{"class":139},[133,228,229],{"class":143}," heap",[133,231,232],{"class":147},"() ",[133,234,235],{"class":139},"*int",[133,237,238],{"class":147}," {\n",[133,240,242,244,246],{"class":135,"line":241},8,[133,243,154],{"class":147},[133,245,157],{"class":139},[133,247,248],{"class":160}," 42\n",[133,250,252,255,258,261],{"class":135,"line":251},9,[133,253,254],{"class":139},"    return",[133,256,257],{"class":139}," &",[133,259,260],{"class":147},"x            ",[133,262,263],{"class":164},"\u002F\u002F куча: указатель уходит наверх\n",[133,265,267],{"class":135,"line":266},10,[133,268,214],{"class":147},[133,270,272],{"class":135,"line":271},11,[133,273,221],{"emptyLinePlaceholder":220},[133,275,277,279,282],{"class":135,"line":276},12,[133,278,140],{"class":139},[133,280,281],{"class":143}," alsoHeap",[133,283,148],{"class":147},[133,285,287,290,292],{"class":135,"line":286},13,[133,288,289],{"class":147},"    n ",[133,291,157],{"class":139},[133,293,294],{"class":160}," 100\n",[133,296,298,300,302],{"class":135,"line":297},14,[133,299,154],{"class":147},[133,301,157],{"class":139},[133,303,248],{"class":160},[133,305,307,310,312,315,318,320,323],{"class":135,"line":306},15,[133,308,309],{"class":147},"    s ",[133,311,157],{"class":139},[133,313,314],{"class":143}," make",[133,316,317],{"class":147},"([]",[133,319,185],{"class":139},[133,321,322],{"class":147},", n)  ",[133,324,325],{"class":164},"\u002F\u002F может быть стек или куча: зависит от использования\n",[133,327,329],{"class":135,"line":328},16,[133,330,221],{"emptyLinePlaceholder":220},[133,332,334,337,340,343,346,348,350],{"class":135,"line":333},17,[133,335,336],{"class":139},"    var",[133,338,339],{"class":147}," i ",[133,341,342],{"class":139},"interface",[133,344,345],{"class":147},"{} ",[133,347,200],{"class":139},[133,349,161],{"class":160},[133,351,352],{"class":164},"  \u002F\u002F сам boxing не обязан уходить в кучу\n",[133,354,356],{"class":135,"line":355},18,[133,357,221],{"emptyLinePlaceholder":220},[133,359,361,364,367],{"class":135,"line":360},19,[133,362,363],{"class":139},"    go",[133,365,366],{"class":139}," func",[133,368,148],{"class":147},[133,370,372,375,378],{"class":135,"line":371},20,[133,373,374],{"class":143},"        println",[133,376,377],{"class":147},"(x)       ",[133,379,380],{"class":164},"\u002F\u002F куча: x захвачен closure\n",[133,382,384],{"class":135,"line":383},21,[133,385,386],{"class":147},"    }()\n",[133,388,390,392,394,397,399],{"class":135,"line":389},22,[133,391,197],{"class":147},[133,393,200],{"class":139},[133,395,396],{"class":147}," s; _ ",[133,398,200],{"class":139},[133,400,401],{"class":147}," i\n",[133,403,405],{"class":135,"line":404},23,[133,406,214],{"class":147},[117,408,409,412,413,416],{},[18,410,411],{},"Правило:"," компилятор кладет на стек всё что может. На кучу — только если обязан (escape analysis). Решение зависит не только от строки кода, но и от use-site: возвращается ли значение наружу, сохраняется ли в heap-объект, передаётся ли в горутину, мешает ли размер\u002Fформа значения оптимизации. Проверяйте факты через ",[130,414,415],{},"go build -gcflags=\"-m -l\"",".",[418,419],"hr",{},[11,421,422,425,428,435],{},[14,423,424],{},"область памяти для переменных, пока функция выполняется: вызов = создание frame, return = frame исчез",[14,426,427],{},"аллокация = сдвиг stack pointer, освобождение = сдвиг обратно, GC не нужен",[14,429,430,431,434],{},"каждая горутина имеет ",[18,432,433],{},"свой стек"," (начинается с 2KB, растёт автоматически)",[14,436,437,440],{},[18,438,439],{},"что попадает на стек:"," локальные переменные с известным размером, аргументы функции, адрес возврата",[418,442],{},[117,444,445],{},"Стек — область памяти где живут переменные пока функция выполняется. Вызвал функцию — создался frame с её переменными. Функция вернулась — frame исчез, память свободна.",[123,447,452],{"className":448,"code":450,"language":451},[449],"language-text","stack frame (создается при каждом вызове функции)\n+------------------------+\n| local variables        |  локальные переменные (sum, temp, ...)\n+------------------------+\n| arguments              |  аргументы функции (a, b, ...)\n+------------------------+\n| return address         |  куда вернуться после return\n+------------------------+\n","text",[130,453,450],{"__ignoreMap":5},[117,455,456],{},[18,457,458],{},"Что попадает на стек:",[11,460,461,464,467],{},[14,462,463],{},"локальные переменные с известным размером",[14,465,466],{},"аргументы функции",[14,468,469],{},"адрес возврата",[117,471,472],{},[18,473,474],{},"Почему быстро:",[117,476,477],{},"Аллокация = сдвинуть указатель. Освобождение = сдвинуть указатель обратно. Не нужен GC.",[117,479,480],{},"Каждая горутина имеет свой стек, начинается с 2KB и растет при необходимости.",[418,482],{},[11,484,485,491,494],{},[14,486,487,488],{},"куча = область памяти для данных, которые ",[18,489,490],{},"не могут жить на стеке",[14,492,493],{},"общая для всех горутин (в отличие от стека — у каждой горутины свой)",[14,495,496],{},"медленнее стека: сложный поиск блока + локи + GC для освобождения",[418,498],{},[117,500,501],{},"Куча — область памяти для данных, которые не могут жить на стеке.",[117,503,504],{},"Куча общая для всех горутин, в отличие от стека — у каждой горутины свой.",[117,506,507],{},[18,508,509],{},"Почему медленнее стека:",[117,511,512],{},"Сложный поиск свободного блока (не просто сдвиг указателя).",[117,514,515],{},"Нужна синхронизация: куча общая → локи.",[117,517,518],{},"Нужен GC для освобождения: стек освобождается при return, куча — нет.",[418,520],{},[11,522,523,530,541,567],{},[14,524,525,526,529],{},"escape analysis: компилятор строит ",[18,527,528],{},"взвешенный граф"," и решает — стек или куча, на этапе компиляции",[14,531,532,533,536,537,540],{},"правила ",[18,534,535],{},"одинаковы для ВСЕХ типов",": интерфейсы, структуры, примитивы, ",[130,538,539],{},"new()"," — без исключений",[14,542,543,544],{},"два условия хипа:\n",[545,546,547,550],"ol",{},[14,548,549],{},"ссылка переживает scope,",[14,551,552,553],{},"объект слишком большой\n",[545,554,555,561],{},[14,556,557,560],{},[130,558,559],{},"maxStackVarSize"," — для обычных переменных (~10MB).",[14,562,563,566],{},[130,564,565],{},"maxImplicitStackVarSize"," — для reference types, создаваемых через указатель\u002Fmake (64KB).",[14,568,569,572,573,576,577],{},[130,570,571],{},"go build -gcflags=\"-m\" main.go"," - посмотреть решение компилятора, есть флаги\n",[130,574,575],{},"-m (подробно)"," и ",[130,578,579],{},"-l (без inlinging)",[418,581],{},[117,583,584],{},"Анализ компилятора на этапе компиляции: переменная живёт на стеке или на куче.",[117,586,587],{},"Меньше escapes → меньше аллокаций на куче → реже запускается GC → быстрее.",[589,590,592],"h2",{"id":591},"escape-analysis","Escape analysis",[594,595,597],"h3",{"id":596},"что-было-бы-без-escape-analysis","Что было бы без escape analysis",[117,599,600,601,604,605,608,609,611],{},"Как в C: ",[130,602,603],{},"return &x"," из функции → x жила на стеке → стек разрушился → dangling pointer → segfault. В Go компилятор видит что ",[130,606,607],{},"&x"," уходит наверх и сам переносит x на кучу. Поэтому ",[130,610,603],{}," в Go безопасно.",[594,613,615],{"id":614},"два-условия-escape-на-кучу","Два условия escape на кучу",[117,617,618,621],{},[18,619,620],{},"1. Компилятор не может доказать",", что на объект не ссылаются после выхода из функции:",[123,623,625],{"className":125,"code":624,"language":127,"meta":5,"style":5},"func f() *int {\n    x := 42\n    return &x  \u002F\u002F moved to heap: ссылка уходит наверх\n}\n",[130,626,627,640,648,660],{"__ignoreMap":5},[133,628,629,631,634,636,638],{"class":135,"line":136},[133,630,140],{"class":139},[133,632,633],{"class":143}," f",[133,635,232],{"class":147},[133,637,235],{"class":139},[133,639,238],{"class":147},[133,641,642,644,646],{"class":135,"line":151},[133,643,154],{"class":147},[133,645,157],{"class":139},[133,647,248],{"class":160},[133,649,650,652,654,657],{"class":135,"line":168},[133,651,254],{"class":139},[133,653,257],{"class":139},[133,655,656],{"class":147},"x  ",[133,658,659],{"class":164},"\u002F\u002F moved to heap: ссылка уходит наверх\n",[133,661,662],{"class":135,"line":194},[133,663,214],{"class":147},[117,665,666,669],{},[18,667,668],{},"2. Объект слишком большой"," для стека:",[123,671,673],{"className":125,"code":672,"language":127,"meta":5,"style":5},"var a [10_000_000]int    \u002F\u002F moved to heap: > maxStackVarSize (~10MB)\ns := make([]int, 70000)  \u002F\u002F moved to heap: > maxImplicitStackVarSize (64KB)\n",[130,674,675,693],{"__ignoreMap":5},[133,676,677,680,683,686,688,690],{"class":135,"line":136},[133,678,679],{"class":139},"var",[133,681,682],{"class":147}," a [",[133,684,685],{"class":160},"10_000_000",[133,687,182],{"class":147},[133,689,185],{"class":139},[133,691,692],{"class":164},"    \u002F\u002F moved to heap: > maxStackVarSize (~10MB)\n",[133,694,695,698,700,702,704,706,709,712,715],{"class":135,"line":151},[133,696,697],{"class":147},"s ",[133,699,157],{"class":139},[133,701,314],{"class":143},[133,703,317],{"class":147},[133,705,185],{"class":139},[133,707,708],{"class":147},", ",[133,710,711],{"class":160},"70000",[133,713,714],{"class":147},")  ",[133,716,717],{"class":164},"\u002F\u002F moved to heap: > maxImplicitStackVarSize (64KB)\n",[117,719,720,722,723,566],{},[130,721,559],{}," — для обычных переменных (~10MB). ",[130,724,565],{},[594,726,728],{"id":727},"правила-одинаковы-для-всех-типов","Правила одинаковы для ВСЕХ типов",[123,730,732],{"className":125,"code":731,"language":127,"meta":5,"style":5},"\u002F\u002F Интерфейсы НЕ всегда хип\nvar x interface{} = 42\nprintln(x)         \u002F\u002F ✅ стек: println не использует рефлексию\n\nfmt.Println(x)     \u002F\u002F ❌ хип: fmt использует рефлексию,\n                   \u002F\u002F    компилятор не может доказать безопасность\n",[130,733,734,739,754,765,769,783],{"__ignoreMap":5},[133,735,736],{"class":135,"line":136},[133,737,738],{"class":164},"\u002F\u002F Интерфейсы НЕ всегда хип\n",[133,740,741,743,746,748,750,752],{"class":135,"line":151},[133,742,679],{"class":139},[133,744,745],{"class":147}," x ",[133,747,342],{"class":139},[133,749,345],{"class":147},[133,751,200],{"class":139},[133,753,248],{"class":160},[133,755,756,759,762],{"class":135,"line":168},[133,757,758],{"class":143},"println",[133,760,761],{"class":147},"(x)         ",[133,763,764],{"class":164},"\u002F\u002F ✅ стек: println не использует рефлексию\n",[133,766,767],{"class":135,"line":194},[133,768,221],{"emptyLinePlaceholder":220},[133,770,771,774,777,780],{"class":135,"line":211},[133,772,773],{"class":147},"fmt.",[133,775,776],{"class":143},"Println",[133,778,779],{"class":147},"(x)     ",[133,781,782],{"class":164},"\u002F\u002F ❌ хип: fmt использует рефлексию,\n",[133,784,785],{"class":135,"line":217},[133,786,787],{"class":164},"                   \u002F\u002F    компилятор не может доказать безопасность\n",[123,789,791],{"className":125,"code":790,"language":127,"meta":5,"style":5},"\u002F\u002F new() НЕ всегда хип\nfunc f() {\n    p := new(int)  \u002F\u002F ✅ стек: указатель не уходит из функции\n    *p = 42\n}\n\nfunc g() *int {\n    p := new(int)  \u002F\u002F ❌ хип: указатель возвращается наверх\n    return p\n}\n",[130,792,793,798,806,826,838,842,846,859,876,883],{"__ignoreMap":5},[133,794,795],{"class":135,"line":136},[133,796,797],{"class":164},"\u002F\u002F new() НЕ всегда хип\n",[133,799,800,802,804],{"class":135,"line":151},[133,801,140],{"class":139},[133,803,633],{"class":143},[133,805,148],{"class":147},[133,807,808,811,813,816,819,821,823],{"class":135,"line":168},[133,809,810],{"class":147},"    p ",[133,812,157],{"class":139},[133,814,815],{"class":143}," new",[133,817,818],{"class":147},"(",[133,820,185],{"class":139},[133,822,714],{"class":147},[133,824,825],{"class":164},"\u002F\u002F ✅ стек: указатель не уходит из функции\n",[133,827,828,831,834,836],{"class":135,"line":194},[133,829,830],{"class":139},"    *",[133,832,833],{"class":147},"p ",[133,835,200],{"class":139},[133,837,248],{"class":160},[133,839,840],{"class":135,"line":211},[133,841,214],{"class":147},[133,843,844],{"class":135,"line":217},[133,845,221],{"emptyLinePlaceholder":220},[133,847,848,850,853,855,857],{"class":135,"line":224},[133,849,140],{"class":139},[133,851,852],{"class":143}," g",[133,854,232],{"class":147},[133,856,235],{"class":139},[133,858,238],{"class":147},[133,860,861,863,865,867,869,871,873],{"class":135,"line":241},[133,862,810],{"class":147},[133,864,157],{"class":139},[133,866,815],{"class":143},[133,868,818],{"class":147},[133,870,185],{"class":139},[133,872,714],{"class":147},[133,874,875],{"class":164},"\u002F\u002F ❌ хип: указатель возвращается наверх\n",[133,877,878,880],{"class":135,"line":251},[133,879,254],{"class":139},[133,881,882],{"class":147}," p\n",[133,884,885],{"class":135,"line":266},[133,886,214],{"class":147},[117,888,889,890,708,893,896],{},"Неважно: ",[130,891,892],{},"new",[130,894,895],{},"make",", литерал, интерфейс — правила escape-анализа идентичны.",[594,898,900],{"id":899},"факты-о-структурах-и-массивах","Факты о структурах и массивах",[117,902,903,904,907,908,911],{},"Если ",[18,905,906],{},"одно поле структуры"," → хип, вся структура → хип. Если ",[18,909,910],{},"один элемент массива\u002Fсреза"," → хип, весь массив\u002Fсрез → хип. Не может часть объекта жить на стеке, а часть в куче.",[594,913,915],{"id":914},"как-посмотреть-решения-компилятора","Как посмотреть решения компилятора",[123,917,921],{"className":918,"code":919,"language":920,"meta":5,"style":5},"language-bash shiki shiki-themes github-dark","go build -gcflags=\"-m\" main.go        # базовый вывод\ngo build -gcflags=\"-m -m\" main.go     # подробный (почему решил)\ngo build -gcflags=\"-m -l\" main.go     # -l отключает inlining (чище вывод)\n\n\n.\u002Fmain.go:5:2: moved to heap: x       # ушла на кучу\n.\u002Fmain.go:9:6: x does not escape      # осталась на стеке\n","bash",[130,922,923,943,959,975,979,983,1003],{"__ignoreMap":5},[133,924,925,927,931,934,937,940],{"class":135,"line":136},[133,926,127],{"class":143},[133,928,930],{"class":929},"sU2Wk"," build",[133,932,933],{"class":160}," -gcflags=",[133,935,936],{"class":929},"\"-m\"",[133,938,939],{"class":929}," main.go",[133,941,942],{"class":164},"        # базовый вывод\n",[133,944,945,947,949,951,954,956],{"class":135,"line":151},[133,946,127],{"class":143},[133,948,930],{"class":929},[133,950,933],{"class":160},[133,952,953],{"class":929},"\"-m -m\"",[133,955,939],{"class":929},[133,957,958],{"class":164},"     # подробный (почему решил)\n",[133,960,961,963,965,967,970,972],{"class":135,"line":168},[133,962,127],{"class":143},[133,964,930],{"class":929},[133,966,933],{"class":160},[133,968,969],{"class":929},"\"-m -l\"",[133,971,939],{"class":929},[133,973,974],{"class":164},"     # -l отключает inlining (чище вывод)\n",[133,976,977],{"class":135,"line":194},[133,978,221],{"emptyLinePlaceholder":220},[133,980,981],{"class":135,"line":211},[133,982,221],{"emptyLinePlaceholder":220},[133,984,985,988,991,994,997,1000],{"class":135,"line":217},[133,986,987],{"class":143},".\u002Fmain.go:5:2:",[133,989,990],{"class":929}," moved",[133,992,993],{"class":929}," to",[133,995,996],{"class":929}," heap:",[133,998,999],{"class":929}," x",[133,1001,1002],{"class":164},"       # ушла на кучу\n",[133,1004,1005,1008,1010,1013,1016,1019],{"class":135,"line":224},[133,1006,1007],{"class":143},".\u002Fmain.go:9:6:",[133,1009,999],{"class":929},[133,1011,1012],{"class":929}," does",[133,1014,1015],{"class":929}," not",[133,1017,1018],{"class":929}," escape",[133,1020,1021],{"class":164},"      # осталась на стеке\n",[418,1023],{},[11,1025,1026,1029,1032],{},[14,1027,1028],{},"принцип: компилятор не может доказать, что переменная не переживёт scope → куча",[14,1030,1031],{},"основные триггеры: return указателя, closure, interface boxing, канал, глобальная переменная, слайс, слишком большой объект, запись указателя в map\u002Fslice",[14,1033,1034,1037,1038,1041],{},[130,1035,1036],{},"make([]int, n)"," с runtime-размером → куча; ",[130,1039,1040],{},"make([]int, 3)"," с константой → стек",[418,1043],{},[117,1045,1046],{},"Принцип: если компилятор не может доказать что переменная не переживёт свой scope — она уходит на кучу.",[123,1048,1050],{"className":125,"code":1049,"language":127,"meta":5,"style":5},"\u002F\u002F Указатель уходит из функции\nfunc f() *int {\n    x := 42\n    return &x              \u002F\u002F escape: указатель переживёт стек функции\n}\n\u002F\u002F Return указателя на локальную переменную → escape: указатель \n\u002F\u002F переживёт стек функции. \n",[130,1051,1052,1057,1069,1077,1089,1093,1098],{"__ignoreMap":5},[133,1053,1054],{"class":135,"line":136},[133,1055,1056],{"class":164},"\u002F\u002F Указатель уходит из функции\n",[133,1058,1059,1061,1063,1065,1067],{"class":135,"line":151},[133,1060,140],{"class":139},[133,1062,633],{"class":143},[133,1064,232],{"class":147},[133,1066,235],{"class":139},[133,1068,238],{"class":147},[133,1070,1071,1073,1075],{"class":135,"line":168},[133,1072,154],{"class":147},[133,1074,157],{"class":139},[133,1076,248],{"class":160},[133,1078,1079,1081,1083,1086],{"class":135,"line":194},[133,1080,254],{"class":139},[133,1082,257],{"class":139},[133,1084,1085],{"class":147},"x              ",[133,1087,1088],{"class":164},"\u002F\u002F escape: указатель переживёт стек функции\n",[133,1090,1091],{"class":135,"line":211},[133,1092,214],{"class":147},[133,1094,1095],{"class":135,"line":217},[133,1096,1097],{"class":164},"\u002F\u002F Return указателя на локальную переменную → escape: указатель \n",[133,1099,1100],{"class":135,"line":224},[133,1101,1102],{"class":164},"\u002F\u002F переживёт стек функции.\n",[123,1104,1106],{"className":125,"code":1105,"language":127,"meta":5,"style":5},"\u002F\u002F Closure захватывает переменную\nfunc f() {\n    x := 42\n    go func() {\n        fmt.Println(x)     \u002F\u002F escape: горутина может жить дольше f()\n    }()\n}\n\u002F\u002FClosure (особенно в горутине) захватывает переменную → escape: \n\u002F\u002Fгорутина может жить дольше f(). \n",[130,1107,1108,1113,1121,1129,1137,1149,1153,1157,1162],{"__ignoreMap":5},[133,1109,1110],{"class":135,"line":136},[133,1111,1112],{"class":164},"\u002F\u002F Closure захватывает переменную\n",[133,1114,1115,1117,1119],{"class":135,"line":151},[133,1116,140],{"class":139},[133,1118,633],{"class":143},[133,1120,148],{"class":147},[133,1122,1123,1125,1127],{"class":135,"line":168},[133,1124,154],{"class":147},[133,1126,157],{"class":139},[133,1128,248],{"class":160},[133,1130,1131,1133,1135],{"class":135,"line":194},[133,1132,363],{"class":139},[133,1134,366],{"class":139},[133,1136,148],{"class":147},[133,1138,1139,1142,1144,1146],{"class":135,"line":211},[133,1140,1141],{"class":147},"        fmt.",[133,1143,776],{"class":143},[133,1145,779],{"class":147},[133,1147,1148],{"class":164},"\u002F\u002F escape: горутина может жить дольше f()\n",[133,1150,1151],{"class":135,"line":217},[133,1152,386],{"class":147},[133,1154,1155],{"class":135,"line":224},[133,1156,214],{"class":147},[133,1158,1159],{"class":135,"line":241},[133,1160,1161],{"class":164},"\u002F\u002FClosure (особенно в горутине) захватывает переменную → escape: \n",[133,1163,1164],{"class":135,"line":251},[133,1165,1166],{"class":164},"\u002F\u002Fгорутина может жить дольше f().\n",[123,1168,1170],{"className":125,"code":1169,"language":127,"meta":5,"style":5},"package main\n\nimport \"fmt\"\n\nfunc main() {\n    \u002F\u002F Interface boxing\n    x := 42\n    var i interface{} = x  \u002F\u002F escape: компилятор не знает размер за интерфейсом\n    fmt.Println(i)         \u002F\u002F аргументы упаковываются в interface{} → куча\n}\n",[130,1171,1172,1180,1184,1198,1202,1211,1216,1224,1242,1255],{"__ignoreMap":5},[133,1173,1174,1177],{"class":135,"line":136},[133,1175,1176],{"class":139},"package",[133,1178,1179],{"class":143}," main\n",[133,1181,1182],{"class":135,"line":151},[133,1183,221],{"emptyLinePlaceholder":220},[133,1185,1186,1189,1192,1195],{"class":135,"line":168},[133,1187,1188],{"class":139},"import",[133,1190,1191],{"class":929}," \"",[133,1193,1194],{"class":143},"fmt",[133,1196,1197],{"class":929},"\"\n",[133,1199,1200],{"class":135,"line":194},[133,1201,221],{"emptyLinePlaceholder":220},[133,1203,1204,1206,1209],{"class":135,"line":211},[133,1205,140],{"class":139},[133,1207,1208],{"class":143}," main",[133,1210,148],{"class":147},[133,1212,1213],{"class":135,"line":217},[133,1214,1215],{"class":164},"    \u002F\u002F Interface boxing\n",[133,1217,1218,1220,1222],{"class":135,"line":224},[133,1219,154],{"class":147},[133,1221,157],{"class":139},[133,1223,248],{"class":160},[133,1225,1226,1228,1230,1232,1234,1236,1239],{"class":135,"line":241},[133,1227,336],{"class":139},[133,1229,339],{"class":147},[133,1231,342],{"class":139},[133,1233,345],{"class":147},[133,1235,200],{"class":139},[133,1237,1238],{"class":147}," x  ",[133,1240,1241],{"class":164},"\u002F\u002F escape: компилятор не знает размер за интерфейсом\n",[133,1243,1244,1247,1249,1252],{"class":135,"line":251},[133,1245,1246],{"class":147},"    fmt.",[133,1248,776],{"class":143},[133,1250,1251],{"class":147},"(i)         ",[133,1253,1254],{"class":164},"\u002F\u002F аргументы упаковываются в interface{} → куча\n",[133,1256,1257],{"class":135,"line":266},[133,1258,214],{"class":147},[117,1260,1261,1262,1265],{},"Interface boxing → escape: компилятор не знает тип\u002Fразмер за интерфейсом. Любой вызов ",[130,1263,1264],{},"fmt.Print\u002FPrintln\u002FSprintf"," → куча.",[123,1267,1269],{"className":125,"code":1268,"language":127,"meta":5,"style":5},"\u002F\u002F Слайс vs массив\ns := []int{1, 2, 3}       \u002F\u002F escape: слайс содержит указатель на underlying array\na := [3]int{1, 2, 3}      \u002F\u002F НЕ escape: массив — value type, копируется целиком\n",[130,1270,1271,1276,1309],{"__ignoreMap":5},[133,1272,1273],{"class":135,"line":136},[133,1274,1275],{"class":164},"\u002F\u002F Слайс vs массив\n",[133,1277,1278,1280,1282,1285,1287,1290,1293,1295,1298,1300,1303,1306],{"class":135,"line":151},[133,1279,697],{"class":147},[133,1281,157],{"class":139},[133,1283,1284],{"class":147}," []",[133,1286,185],{"class":139},[133,1288,1289],{"class":147},"{",[133,1291,1292],{"class":160},"1",[133,1294,708],{"class":147},[133,1296,1297],{"class":160},"2",[133,1299,708],{"class":147},[133,1301,1302],{"class":160},"3",[133,1304,1305],{"class":147},"}       ",[133,1307,1308],{"class":164},"\u002F\u002F escape: слайс содержит указатель на underlying array\n",[133,1310,1311,1314,1316,1318,1320,1322,1324,1326,1328,1330,1332,1334,1336,1339],{"class":135,"line":168},[133,1312,1313],{"class":147},"a ",[133,1315,157],{"class":139},[133,1317,176],{"class":147},[133,1319,1302],{"class":160},[133,1321,182],{"class":147},[133,1323,185],{"class":139},[133,1325,1289],{"class":147},[133,1327,1292],{"class":160},[133,1329,708],{"class":147},[133,1331,1297],{"class":160},[133,1333,708],{"class":147},[133,1335,1302],{"class":160},[133,1337,1338],{"class":147},"}      ",[133,1340,1341],{"class":164},"\u002F\u002F НЕ escape: массив — value type, копируется целиком\n",[117,1343,1344],{},"Слайс → escape (содержит указатель на underlying array). Массив фиксированного размера → НЕ escape (value type, копируется).",[123,1346,1348],{"className":125,"code":1347,"language":127,"meta":5,"style":5},"\u002F\u002F Слишком большой объект\nvar a [10_000_000]int      \u002F\u002F escape: не влезает в стек, даже без return &a\n",[130,1349,1350,1355],{"__ignoreMap":5},[133,1351,1352],{"class":135,"line":136},[133,1353,1354],{"class":164},"\u002F\u002F Слишком большой объект\n",[133,1356,1357,1359,1361,1363,1365,1367],{"class":135,"line":151},[133,1358,679],{"class":139},[133,1360,682],{"class":147},[133,1362,685],{"class":160},[133,1364,182],{"class":147},[133,1366,185],{"class":139},[133,1368,1369],{"class":164},"      \u002F\u002F escape: не влезает в стек, даже без return &a\n",[117,1371,1372],{},"Слишком большой объект → escape: не влезает в стек даже без return.",[123,1374,1376],{"className":125,"code":1375,"language":127,"meta":5,"style":5},"\u002F\u002F Передача в канал\nch \u003C- &x                   \u002F\u002F escape: компилятор не знает кто и когда прочитает\n",[130,1377,1378,1383],{"__ignoreMap":5},[133,1379,1380],{"class":135,"line":136},[133,1381,1382],{"class":164},"\u002F\u002F Передача в канал\n",[133,1384,1385,1388,1391,1393,1396],{"class":135,"line":151},[133,1386,1387],{"class":147},"ch ",[133,1389,1390],{"class":139},"\u003C-",[133,1392,257],{"class":139},[133,1394,1395],{"class":147},"x                   ",[133,1397,1398],{"class":164},"\u002F\u002F escape: компилятор не знает кто и когда прочитает\n",[117,1400,1401],{},"Передача указателя в канал → escape: компилятор не знает кто и когда прочитает.",[123,1403,1405],{"className":125,"code":1404,"language":127,"meta":5,"style":5},"\u002F\u002F make с неизвестным размером\ns := make([]int, n)        \u002F\u002F escape: n известен только в runtime\ns := make([]int, 3)        \u002F\u002F НЕ escape: размер известен компилятору\n",[130,1406,1407,1412,1430],{"__ignoreMap":5},[133,1408,1409],{"class":135,"line":136},[133,1410,1411],{"class":164},"\u002F\u002F make с неизвестным размером\n",[133,1413,1414,1416,1418,1420,1422,1424,1427],{"class":135,"line":151},[133,1415,697],{"class":147},[133,1417,157],{"class":139},[133,1419,314],{"class":143},[133,1421,317],{"class":147},[133,1423,185],{"class":139},[133,1425,1426],{"class":147},", n)        ",[133,1428,1429],{"class":164},"\u002F\u002F escape: n известен только в runtime\n",[133,1431,1432,1434,1436,1438,1440,1442,1444,1446,1449],{"class":135,"line":168},[133,1433,697],{"class":147},[133,1435,157],{"class":139},[133,1437,314],{"class":143},[133,1439,317],{"class":147},[133,1441,185],{"class":139},[133,1443,708],{"class":147},[133,1445,1302],{"class":160},[133,1447,1448],{"class":147},")        ",[133,1450,1451],{"class":164},"\u002F\u002F НЕ escape: размер известен компилятору\n",[117,1453,1454,1456,1457,1459],{},[130,1455,1036],{}," с runtime-размером → escape. ",[130,1458,1040],{}," с константой → НЕ escape.",[123,1461,1463],{"className":125,"code":1462,"language":127,"meta":5,"style":5},"\u002F\u002F Запись в глобальную переменную\nvar global *int\nfunc f() {\n    x := 1\n    global = &x            \u002F\u002F escape: переживает scope функции\n}\n",[130,1464,1465,1470,1480,1488,1497,1511],{"__ignoreMap":5},[133,1466,1467],{"class":135,"line":136},[133,1468,1469],{"class":164},"\u002F\u002F Запись в глобальную переменную\n",[133,1471,1472,1474,1477],{"class":135,"line":151},[133,1473,679],{"class":139},[133,1475,1476],{"class":147}," global ",[133,1478,1479],{"class":139},"*int\n",[133,1481,1482,1484,1486],{"class":135,"line":168},[133,1483,140],{"class":139},[133,1485,633],{"class":143},[133,1487,148],{"class":147},[133,1489,1490,1492,1494],{"class":135,"line":194},[133,1491,154],{"class":147},[133,1493,157],{"class":139},[133,1495,1496],{"class":160}," 1\n",[133,1498,1499,1502,1504,1506,1508],{"class":135,"line":211},[133,1500,1501],{"class":147},"    global ",[133,1503,200],{"class":139},[133,1505,257],{"class":139},[133,1507,260],{"class":147},[133,1509,1510],{"class":164},"\u002F\u002F escape: переживает scope функции\n",[133,1512,1513],{"class":135,"line":217},[133,1514,214],{"class":147},[117,1516,1517],{},"Запись указателя в глобальную переменную → escape: переживает scope функции.",[123,1519,1521],{"className":125,"code":1520,"language":127,"meta":5,"style":5},"\u002F\u002F Запись указателя в map\u002Fslice\nm[key] = &x               \u002F\u002F escape: компилятор не может отследить lifetime\nslice = append(slice, &x)  \u002F\u002F escape: то же самое\n",[130,1522,1523,1528,1543],{"__ignoreMap":5},[133,1524,1525],{"class":135,"line":136},[133,1526,1527],{"class":164},"\u002F\u002F Запись указателя в map\u002Fslice\n",[133,1529,1530,1533,1535,1537,1540],{"class":135,"line":151},[133,1531,1532],{"class":147},"m[key] ",[133,1534,200],{"class":139},[133,1536,257],{"class":139},[133,1538,1539],{"class":147},"x               ",[133,1541,1542],{"class":164},"\u002F\u002F escape: компилятор не может отследить lifetime\n",[133,1544,1545,1548,1550,1553,1556,1559,1562],{"class":135,"line":168},[133,1546,1547],{"class":147},"slice ",[133,1549,200],{"class":139},[133,1551,1552],{"class":143}," append",[133,1554,1555],{"class":147},"(slice, ",[133,1557,1558],{"class":139},"&",[133,1560,1561],{"class":147},"x)  ",[133,1563,1564],{"class":164},"\u002F\u002F escape: то же самое\n",[117,1566,1567],{},"Запись указателя в map или slice → escape: компилятор не может отследить lifetime.",[418,1569],{},[11,1571,1572,1578,1584],{},[14,1573,1574,1577],{},[18,1575,1576],{},"mcache"," — per-P, без локов. Большинство аллокаций здесь",[14,1579,1580,1583],{},[18,1581,1582],{},"mcentral"," — по одному на каждый size class (67 штук), с локами",[14,1585,1586,1589],{},[18,1587,1588],{},"mheap"," — один глобальный, раздаёт страницы из арен",[418,1591],{},[117,1593,1594],{},[18,1595,1596],{},"Зачем три уровня:",[117,1598,1599],{},"Если все горутины лезут за памятью в одно место → contention (все ждут один лок). Решение — иерархия: сначала быстро, потом медленнее.",[117,1601,1602],{},[18,1603,1604],{},"Почему mcache на P, а не на M (поток):",[117,1606,1607],{},"TCMalloc делал кэш per-thread. Go улучшил: привязал к P. Когда M блокируется на syscall, P с mcache переходит к другому M → память не простаивает.",[418,1609],{},[117,1611,1612,1615,1616],{},[18,1613,1614],{},"Проблема без классов:","\nВыделяешь 5, 20, 3 байта вперемешку. Удалил 20 — дыра. Кто-то просит 25 — не влезает. ",[18,1617,1618],{},"Фрагментация.",[117,1620,1621,1624],{},[18,1622,1623],{},"Решение:","\n67 фиксированных размеров блоков: 8B, 16B, 24B, 32B, 48B... до 32KB.",[117,1626,1627,1628,1631],{},"Объект попадает в ",[18,1629,1630],{},"ближайший подходящий"," класс — просишь 5B, получаешь блок 8B.",[117,1633,1634],{},"Блоки одного размера лежат вместе. Удалил блок 8B → на его место встанет любой другой 8B. Нет фрагментации.",[117,1636,1637,1640],{},[18,1638,1639],{},"Потери памяти:","\nМинус — тратишь лишнее. Просил 5B, получил 8B → 3B пустуют.",[117,1642,1643],{},"Чем меньше объект относительно класса, тем больше потеря: 1B в классе 8B = 87%, 9B в классе 16B = 43%.",[117,1645,1646],{},"Чем крупнее класс — тем меньше потеря: 32KB = всего 6%.",[117,1648,1649,1652],{},[18,1650,1651],{},"Объекты > 32KB:","\nНе помещаются ни в один класс. Аллоцируются напрямую из mheap страницами по 8KB.",[117,1654,1655],{},"Даже +1 байт сверх 32KB = дополнительная страница 8KB.",[123,1657,1660],{"className":1658,"code":1659,"language":451},[449],"Класс  Размер    Max waste     Пример\n  1      8B        87%         1B объект → 7B пустует\n  2     16B        43%         9B объект → 7B пустует\n  3     24B        29%\n  5     48B        31%         33B объект → 15B пустует\n  7     80B        19%         66B объект → 14B пустует\n 67    32KB         6%\n",[130,1661,1659],{"__ignoreMap":5},[418,1663],{},[11,1665,1666,1672,1678,1684],{},[14,1667,1668,1669],{},"Go просит у ОС память большими кусками — ",[18,1670,1671],{},"арены по 64MB",[14,1673,1674,1675],{},"арена нарезается на ",[18,1676,1677],{},"страницы по 8KB",[14,1679,1680,1681],{},"страницы объединяются в ",[18,1682,1683],{},"спаны (mspan)",[14,1685,1686,1687],{},"каждый спан обслуживает ",[18,1688,1689],{},"один класс размеров",[418,1691],{},[117,1693,1694],{},[18,1695,1696],{},"Почему арены, а не маленькие куски:",[117,1698,1699],{},"Каждый запрос к ОС = syscall = дорого. Лучше попросить 64MB один раз, чем 1KB тысячу раз.",[117,1701,1702],{},[18,1703,1704],{},"Цепочка:",[123,1706,1709],{"className":1707,"code":1708,"language":451},[449],"Go запрашивает у ОС арену (64MB на Linux) \n    → арена нарезается на страницы (8KB каждая) \n        → страницы объединяются в спаны (mspan) \n            → каждый спан обслуживает один size class \n",[130,1710,1708],{"__ignoreMap":5},[117,1712,1713,1716],{},[18,1714,1715],{},"Спан (mspan)"," — ключевое понятие:",[123,1718,1721],{"className":1719,"code":1720,"language":451},[449],"Спан для класса 8B (1 страница = 8KB):\n[8B][8B][8B][8B][8B]...[8B]  — 1024 блока по 8 байт\n\nСпан для класса 24B (1 страница = 8KB):\n[24B][24B][24B]...[24B]  — 341 блок по 24 байт\n\nСпан для класса 32KB (4 страницы = 32KB):\n[32KB]  — 1 блок\n",[130,1722,1720],{"__ignoreMap":5},[117,1724,1725],{},"Спан = группа страниц, нарезанная на одинаковые блоки. Размер блока = класс размеров.",[418,1727],{},[11,1729,1730,1733,1740,1743],{},[14,1731,1732],{},"CPU читает блоками по 4\u002F8 байт: данные на «кривом» адресе = два чтения или ошибка",[14,1734,1735,1736,1739],{},"компилятор вставляет ",[18,1737,1738],{},"padding"," (пустые байты) для выравнивания полей",[14,1741,1742],{},"порядок полей влияет на размер структуры: сортируй от большего к меньшему",[14,1744,1745],{},"размер структуры должен быть кратен выравниванию самого большого поля (для корректной работы массивов)",[123,1747,1750],{"className":1748,"code":1749,"language":451},[449],"тип      размер   выравнивание (адрес должен быть кратен)\n------   ------   -----------------------------------------\nbool     1        1  (любой адрес ок)\nint8     1        1\nint16    2        2  (адреса 0, 2, 4, 6, 8...)\nint32    4        4  (адреса 0, 4, 8, 12...)\nint64    8        8  (адреса 0, 8, 16, 24...)\npointer  8        8\n",[130,1751,1749],{"__ignoreMap":5},[418,1753],{},[589,1755,1757],{"id":1756},"выравнивание-и-padding","Выравнивание и padding",[594,1759,1761],{"id":1760},"почему-нужно-выравнивание","Почему нужно выравнивание",[117,1763,1764],{},"CPU не читает память по одному байту — он читает блоками по 4 или 8 байт. Если int64 лежит на адресе кратном 8 — одно чтение. Если на \"кривом\" адресе — два чтения и склейка, или вообще ошибка.",[123,1766,1769],{"className":1767,"code":1768,"language":451},[449],"Память (каждая ячейка = 1 байт):\nадрес:  0  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15\n        [--------блок 1--------][--------блок 2-------]\n\nint64 на адресе 0: читается за 1 раз (адрес кратен 8)\nint64 на адресе 3: частично в блоке 1, частично в блоке 2 -- плохо\n",[130,1770,1768],{"__ignoreMap":5},[594,1772,1773],{"id":1738},"Padding",[117,1775,1776],{},[18,1777,1778],{},"Поэтому компилятор вставляет пустые байты (padding):",[123,1780,1782],{"className":125,"code":1781,"language":127,"meta":5,"style":5},"type Bad struct {\n    a bool    \u002F\u002F 1 байт, адрес 0\n    b int64   \u002F\u002F 8 байт, но адрес 1 не кратен 8!\n}\n\n\u002F\u002F Компилятор делает так:\n\u002F\u002F a     [1 байт]  адрес 0\n\u002F\u002F pad   [7 байт]  адреса 1-7 (пустота)\n\u002F\u002F b     [8 байт]  адрес 8 (кратен 8, ок)\n\u002F\u002F итого: 16 байт вместо 9\n",[130,1783,1784,1797,1808,1819,1823,1827,1832,1837,1842,1847],{"__ignoreMap":5},[133,1785,1786,1789,1792,1795],{"class":135,"line":136},[133,1787,1788],{"class":139},"type",[133,1790,1791],{"class":143}," Bad",[133,1793,1794],{"class":139}," struct",[133,1796,238],{"class":147},[133,1798,1799,1802,1805],{"class":135,"line":151},[133,1800,1801],{"class":147},"    a ",[133,1803,1804],{"class":139},"bool",[133,1806,1807],{"class":164},"    \u002F\u002F 1 байт, адрес 0\n",[133,1809,1810,1813,1816],{"class":135,"line":168},[133,1811,1812],{"class":147},"    b ",[133,1814,1815],{"class":139},"int64",[133,1817,1818],{"class":164},"   \u002F\u002F 8 байт, но адрес 1 не кратен 8!\n",[133,1820,1821],{"class":135,"line":194},[133,1822,214],{"class":147},[133,1824,1825],{"class":135,"line":211},[133,1826,221],{"emptyLinePlaceholder":220},[133,1828,1829],{"class":135,"line":217},[133,1830,1831],{"class":164},"\u002F\u002F Компилятор делает так:\n",[133,1833,1834],{"class":135,"line":224},[133,1835,1836],{"class":164},"\u002F\u002F a     [1 байт]  адрес 0\n",[133,1838,1839],{"class":135,"line":241},[133,1840,1841],{"class":164},"\u002F\u002F pad   [7 байт]  адреса 1-7 (пустота)\n",[133,1843,1844],{"class":135,"line":251},[133,1845,1846],{"class":164},"\u002F\u002F b     [8 байт]  адрес 8 (кратен 8, ок)\n",[133,1848,1849],{"class":135,"line":266},[133,1850,1851],{"class":164},"\u002F\u002F итого: 16 байт вместо 9\n",[594,1853,1855],{"id":1854},"влияние-порядка-полей-на-размер-структуры","Влияние порядка полей на размер структуры",[117,1857,1858],{},[18,1859,1860],{},"Порядок полей влияет на размер:",[123,1862,1864],{"className":125,"code":1863,"language":127,"meta":5,"style":5},"type Bad struct {     \u002F\u002F 24 байта\n    a bool            \u002F\u002F 0: [a][ ][ ][ ][ ][ ][ ][ ]\n    b int64           \u002F\u002F 8: [b][b][b][b][b][b][b][b]\n    c bool            \u002F\u002F 16:[c][ ][ ][ ][ ][ ][ ][ ]\n}\n\ntype Good struct {    \u002F\u002F 16 байт\n    b int64           \u002F\u002F 0: [b][b][b][b][b][b][b][b]\n    a bool            \u002F\u002F 8: [a]\n    c bool            \u002F\u002F 9: [c][ ][ ][ ][ ][ ][ ]\n}\n",[130,1865,1866,1880,1889,1898,1908,1912,1916,1931,1940,1949,1958],{"__ignoreMap":5},[133,1867,1868,1870,1872,1874,1877],{"class":135,"line":136},[133,1869,1788],{"class":139},[133,1871,1791],{"class":143},[133,1873,1794],{"class":139},[133,1875,1876],{"class":147}," {     ",[133,1878,1879],{"class":164},"\u002F\u002F 24 байта\n",[133,1881,1882,1884,1886],{"class":135,"line":151},[133,1883,1801],{"class":147},[133,1885,1804],{"class":139},[133,1887,1888],{"class":164},"            \u002F\u002F 0: [a][ ][ ][ ][ ][ ][ ][ ]\n",[133,1890,1891,1893,1895],{"class":135,"line":168},[133,1892,1812],{"class":147},[133,1894,1815],{"class":139},[133,1896,1897],{"class":164},"           \u002F\u002F 8: [b][b][b][b][b][b][b][b]\n",[133,1899,1900,1903,1905],{"class":135,"line":194},[133,1901,1902],{"class":147},"    c ",[133,1904,1804],{"class":139},[133,1906,1907],{"class":164},"            \u002F\u002F 16:[c][ ][ ][ ][ ][ ][ ][ ]\n",[133,1909,1910],{"class":135,"line":211},[133,1911,214],{"class":147},[133,1913,1914],{"class":135,"line":217},[133,1915,221],{"emptyLinePlaceholder":220},[133,1917,1918,1920,1923,1925,1928],{"class":135,"line":224},[133,1919,1788],{"class":139},[133,1921,1922],{"class":143}," Good",[133,1924,1794],{"class":139},[133,1926,1927],{"class":147}," {    ",[133,1929,1930],{"class":164},"\u002F\u002F 16 байт\n",[133,1932,1933,1935,1937],{"class":135,"line":241},[133,1934,1812],{"class":147},[133,1936,1815],{"class":139},[133,1938,1939],{"class":164},"           \u002F\u002F 0: [b][b][b][b][b][b][b][b]\n",[133,1941,1942,1944,1946],{"class":135,"line":251},[133,1943,1801],{"class":147},[133,1945,1804],{"class":139},[133,1947,1948],{"class":164},"            \u002F\u002F 8: [a]\n",[133,1950,1951,1953,1955],{"class":135,"line":266},[133,1952,1902],{"class":147},[133,1954,1804],{"class":139},[133,1956,1957],{"class":164},"            \u002F\u002F 9: [c][ ][ ][ ][ ][ ][ ]\n",[133,1959,1960],{"class":135,"line":271},[133,1961,214],{"class":147},[117,1963,1964,1966],{},[18,1965,411],{}," сортируй поля от большего к меньшему — меньше padding, меньше памяти.",[594,1968,1970],{"id":1969},"кратность-размера-структуры-выравниванию","Кратность размера структуры выравниванию",[123,1972,1974],{"className":125,"code":1973,"language":127,"meta":5,"style":5},"type A struct {\n    a int64   \u002F\u002F 0-7\n    b int32   \u002F\u002F 8-11\n}\n\u002F\u002F Данные = 12 байт. Самое большое поле int64 → кратно 8\n\u002F\u002F unsafe.Sizeof(A{}) = 16 (12 → 16)\n\ntype B struct {\n    a int32   \u002F\u002F 0-3\n    b int16   \u002F\u002F 4-5\n}\n\u002F\u002F Данные = 6 байт. Самое большое поле int32 → кратно 4\n\u002F\u002F unsafe.Sizeof(B{}) = 8 (6 → 8)\n",[130,1975,1976,1987,1996,2006,2010,2015,2020,2024,2035,2044,2054,2058,2063],{"__ignoreMap":5},[133,1977,1978,1980,1983,1985],{"class":135,"line":136},[133,1979,1788],{"class":139},[133,1981,1982],{"class":143}," A",[133,1984,1794],{"class":139},[133,1986,238],{"class":147},[133,1988,1989,1991,1993],{"class":135,"line":151},[133,1990,1801],{"class":147},[133,1992,1815],{"class":139},[133,1994,1995],{"class":164},"   \u002F\u002F 0-7\n",[133,1997,1998,2000,2003],{"class":135,"line":168},[133,1999,1812],{"class":147},[133,2001,2002],{"class":139},"int32",[133,2004,2005],{"class":164},"   \u002F\u002F 8-11\n",[133,2007,2008],{"class":135,"line":194},[133,2009,214],{"class":147},[133,2011,2012],{"class":135,"line":211},[133,2013,2014],{"class":164},"\u002F\u002F Данные = 12 байт. Самое большое поле int64 → кратно 8\n",[133,2016,2017],{"class":135,"line":217},[133,2018,2019],{"class":164},"\u002F\u002F unsafe.Sizeof(A{}) = 16 (12 → 16)\n",[133,2021,2022],{"class":135,"line":224},[133,2023,221],{"emptyLinePlaceholder":220},[133,2025,2026,2028,2031,2033],{"class":135,"line":241},[133,2027,1788],{"class":139},[133,2029,2030],{"class":143}," B",[133,2032,1794],{"class":139},[133,2034,238],{"class":147},[133,2036,2037,2039,2041],{"class":135,"line":251},[133,2038,1801],{"class":147},[133,2040,2002],{"class":139},[133,2042,2043],{"class":164},"   \u002F\u002F 0-3\n",[133,2045,2046,2048,2051],{"class":135,"line":266},[133,2047,1812],{"class":147},[133,2049,2050],{"class":139},"int16",[133,2052,2053],{"class":164},"   \u002F\u002F 4-5\n",[133,2055,2056],{"class":135,"line":271},[133,2057,214],{"class":147},[133,2059,2060],{"class":135,"line":276},[133,2061,2062],{"class":164},"\u002F\u002F Данные = 6 байт. Самое большое поле int32 → кратно 4\n",[133,2064,2065],{"class":135,"line":286},[133,2066,2067],{"class":164},"\u002F\u002F unsafe.Sizeof(B{}) = 8 (6 → 8)\n",[117,2069,2070],{},"Зачем размер структуры должен быть кратен выравниванию самого большого поля — для корректной работы массивов:",[123,2072,2075],{"className":2073,"code":2074,"language":451},[449],"Зачем: массив [2]A\nС padding (размер 16):\n  элемент 0: адреса 0-15\n  элемент 1: адреса 16-31 → int64 на адресе 16, кратен 8 → ок\n\nБез padding (размер 12):\n  элемент 0: адреса 0-11\n  элемент 1: адреса 12-23 → int64 на адресе 12, не кратен 8 → сломано\n",[130,2076,2074],{"__ignoreMap":5},[594,2078,2080],{"id":2079},"как-проверить","Как проверить",[117,2082,2083],{},[18,2084,2085],{},"Как проверить:",[123,2087,2089],{"className":125,"code":2088,"language":127,"meta":5,"style":5},"unsafe.Sizeof(Bad{})      \u002F\u002F 24\nunsafe.Sizeof(Good{})     \u002F\u002F 16\nunsafe.Alignof(x)         \u002F\u002F требование выравнивания типа\nunsafe.Offsetof(s.field)  \u002F\u002F смещение поля от начала структуры\n",[130,2090,2091,2110,2127,2139],{"__ignoreMap":5},[133,2092,2093,2096,2099,2101,2104,2107],{"class":135,"line":136},[133,2094,2095],{"class":147},"unsafe.",[133,2097,2098],{"class":143},"Sizeof",[133,2100,818],{"class":147},[133,2102,2103],{"class":143},"Bad",[133,2105,2106],{"class":147},"{})      ",[133,2108,2109],{"class":164},"\u002F\u002F 24\n",[133,2111,2112,2114,2116,2118,2121,2124],{"class":135,"line":151},[133,2113,2095],{"class":147},[133,2115,2098],{"class":143},[133,2117,818],{"class":147},[133,2119,2120],{"class":143},"Good",[133,2122,2123],{"class":147},"{})     ",[133,2125,2126],{"class":164},"\u002F\u002F 16\n",[133,2128,2129,2131,2134,2136],{"class":135,"line":168},[133,2130,2095],{"class":147},[133,2132,2133],{"class":143},"Alignof",[133,2135,761],{"class":147},[133,2137,2138],{"class":164},"\u002F\u002F требование выравнивания типа\n",[133,2140,2141,2143,2146,2149],{"class":135,"line":194},[133,2142,2095],{"class":147},[133,2144,2145],{"class":143},"Offsetof",[133,2147,2148],{"class":147},"(s.field)  ",[133,2150,2151],{"class":164},"\u002F\u002F смещение поля от начала структуры\n",[418,2153],{},[11,2155,2156,2163,2166,2173],{},[14,2157,2158,2159,2162],{},"стек горутины: 2KB → растёт x2 при нехватке, ",[18,2160,2161],{},"сужается"," x2 при использовании \u003C25%",[14,2164,2165],{},"contiguous stacks (Go 1.4+): копирование в новый блок + обновление указателей (stack maps), нет hot split",[14,2167,2168,2169,2172],{},"uintptr ",[18,2170,2171],{},"не обновляется"," при переносе (семантически не указатель)",[14,2174,2175],{},"макс: ~1GB (64-bit), ~250MB (32-bit). Стек аллоцируется в хипе → может расти",[418,2177],{},[117,2179,2180],{},"Стек горутины начинается с 2KB и растет автоматически при нехватке места.",[117,2182,2183],{},[18,2184,2185],{},"Как работает:",[123,2187,2190],{"className":2188,"code":2189,"language":451},[449],"1. Компилятор вставляет проверку в начало каждой функции (prologue)\n\nfunc foo() {\n    \u002F\u002F [скрытый код] if SP \u003C stackguard { runtime.morestack() }\n    ...\n}\n\n2. Если места мало -- runtime.morestack():\n   - аллоцирует новый стек (2x размер)\n   - копирует данные со старого стека\n   - обновляет все указатели на стек\n     - очищает старый стек\n   - продолжает выполнение\n",[130,2191,2189],{"__ignoreMap":5},[117,2193,2194,2195,416],{},"Компилятор вставляет проверку в prologue каждой функции: ",[130,2196,2197],{},"if SP \u003C stackguard { runtime.morestack() }",[117,2199,2200,2201,2204],{},"При вызове ",[130,2202,2203],{},"runtime.morestack()",": аллоцируется новый стек размером 2x, копируются данные, обновляются все указатели, выполнение продолжается.",[123,2206,2209],{"className":2207,"code":2208,"language":451},[449],"До роста:              После роста:\n+--------+ 2KB         +--------+ 4KB\n| frame1 |             | frame1 |\n| frame2 |             | frame2 |\n| frame3 |             | frame3 |\n+--------+ \u003C- мало     |  free  |\n                       |  free  |\n                       +--------+ \u003C- есть место\n",[130,2210,2208],{"__ignoreMap":5},[117,2212,2213],{},[18,2214,2215],{},"Почему копирование, а не сегменты:",[117,2217,2218],{},"Go 1.3 и раньше использовал segmented stacks (связный список сегментов).",[117,2220,2221],{},"Проблема segmented stacks — «hot split»: функция на границе стека постоянно аллоцирует\u002Fосвобождает сегмент.",[117,2223,2224],{},"Go 1.4+: contiguous stacks (копирование) — дороже разово, но нет hot split.",[117,2226,2227],{},[18,2228,2229],{},"Лимиты:",[11,2231,2232,2235,2238],{},[14,2233,2234],{},"начальный размер: 2KB",[14,2236,2237],{},"максимум: ~1GB (64-bit), ~250MB (32-bit)",[14,2239,2240],{},"рост: x2 каждый раз",[117,2242,2243],{},[18,2244,2245],{},"Указатели фиксятся автоматически:",[123,2247,2249],{"className":125,"code":2248,"language":127,"meta":5,"style":5},"func foo() {\n    x := 42\n    bar(&x)  \u002F\u002F если стек вырастет во время bar(), адрес &x обновится\n}\n",[130,2250,2251,2260,2268,2282],{"__ignoreMap":5},[133,2252,2253,2255,2258],{"class":135,"line":136},[133,2254,140],{"class":139},[133,2256,2257],{"class":143}," foo",[133,2259,148],{"class":147},[133,2261,2262,2264,2266],{"class":135,"line":151},[133,2263,154],{"class":147},[133,2265,157],{"class":139},[133,2267,248],{"class":160},[133,2269,2270,2273,2275,2277,2279],{"class":135,"line":168},[133,2271,2272],{"class":143},"    bar",[133,2274,818],{"class":147},[133,2276,1558],{"class":139},[133,2278,1561],{"class":147},[133,2280,2281],{"class":164},"\u002F\u002F если стек вырастет во время bar(), адрес &x обновится\n",[133,2283,2284],{"class":135,"line":194},[133,2285,214],{"class":147},[117,2287,2288],{},"Runtime знает где на стеке лежат указатели (stack maps от компилятора) и корректирует их при копировании.",[117,2290,2291,2294],{},[130,2292,2293],{},"uintptr"," не обновляется при переносе стека — это число, а не указатель.",[117,2296,2297],{},[18,2298,2299],{},"Сужение стека:",[117,2301,2302],{},"Если горутина использует менее 25% стека, runtime сужает его в 2 раза.",[117,2304,2305],{},"Например: стек 8KB, используется \u003C2KB → сузится до 4KB.",[418,2307],{},[11,2309,2310,2317,2328,2331],{},[14,2311,2312,2313,2316],{},"возврат ",[18,2314,2315],{},"значения"," из конструктора вместо указателя = стек вместо хипа",[14,2318,2319,2320,2323,2324,2327],{},"сигнатура ",[130,2321,2322],{},"Read(buf []byte)"," вместо ",[130,2325,2326],{},"Read() []byte"," = вызывающий контролирует аллокацию",[14,2329,2330],{},"вынос переменной из цикла: даже на стеке — сдвиг pointer + инициализация каждую итерацию",[14,2332,2333,2334,2337],{},"нельзя явно указать «аллоцируй на стеке», но можно ",[18,2335,2336],{},"подсказывать"," escape-анализу",[418,2339],{},[589,2341,2343],{"id":2342},"практические-кейсы-escape-analysis","Практические кейсы escape analysis",[594,2345,2347],{"id":2346},"возврат-значения-vs-указателя","Возврат значения vs указателя",[123,2349,2351],{"className":125,"code":2350,"language":127,"meta":5,"style":5},"\u002F\u002F ⚠️ указатель часто приводит к escape, если переживает scope вызова\nfunc NewUser() *User {\n    return &User{Name: \"Alice\"}  \u002F\u002F часто moved to heap без inlining\n}\n\n\u002F\u002F ✅ значение проще оставить на стеке и скопировать при return\nfunc NewUser() User {\n    return User{Name: \"Alice\"}   \u002F\u002F стек, копируется при return\n}\n",[130,2352,2353,2358,2375,2395,2399,2403,2408,2420,2437],{"__ignoreMap":5},[133,2354,2355],{"class":135,"line":136},[133,2356,2357],{"class":164},"\u002F\u002F ⚠️ указатель часто приводит к escape, если переживает scope вызова\n",[133,2359,2360,2362,2365,2367,2370,2373],{"class":135,"line":151},[133,2361,140],{"class":139},[133,2363,2364],{"class":143}," NewUser",[133,2366,232],{"class":147},[133,2368,2369],{"class":139},"*",[133,2371,2372],{"class":143},"User",[133,2374,238],{"class":147},[133,2376,2377,2379,2381,2383,2386,2389,2392],{"class":135,"line":168},[133,2378,254],{"class":139},[133,2380,257],{"class":139},[133,2382,2372],{"class":143},[133,2384,2385],{"class":147},"{Name: ",[133,2387,2388],{"class":929},"\"Alice\"",[133,2390,2391],{"class":147},"}  ",[133,2393,2394],{"class":164},"\u002F\u002F часто moved to heap без inlining\n",[133,2396,2397],{"class":135,"line":194},[133,2398,214],{"class":147},[133,2400,2401],{"class":135,"line":211},[133,2402,221],{"emptyLinePlaceholder":220},[133,2404,2405],{"class":135,"line":217},[133,2406,2407],{"class":164},"\u002F\u002F ✅ значение проще оставить на стеке и скопировать при return\n",[133,2409,2410,2412,2414,2416,2418],{"class":135,"line":224},[133,2411,140],{"class":139},[133,2413,2364],{"class":143},[133,2415,232],{"class":147},[133,2417,2372],{"class":143},[133,2419,238],{"class":147},[133,2421,2422,2424,2427,2429,2431,2434],{"class":135,"line":241},[133,2423,254],{"class":139},[133,2425,2426],{"class":143}," User",[133,2428,2385],{"class":147},[133,2430,2388],{"class":929},[133,2432,2433],{"class":147},"}   ",[133,2435,2436],{"class":164},"\u002F\u002F стек, копируется при return\n",[133,2438,2439],{"class":135,"line":251},[133,2440,214],{"class":147},[117,2442,2443],{},"Это не железное правило: inlining и дальнейшее использование результата могут убрать аллокацию даже для функции, возвращающей указатель. Доверяйте не форме кода, а отчёту escape analysis и benchmark.",[117,2445,2446],{},"Бенчмарк: разница существенная (аллокация + нагрузка на GC).",[117,2448,2449],{},"Не значит что всегда нужно возвращать значение. Но если создаёте огромное количество объектов — стоит задуматься.",[594,2451,2453],{"id":2452},"сигнатура-read","Сигнатура Read",[123,2455,2457],{"className":125,"code":2456,"language":127,"meta":5,"style":5},"\u002F\u002F ❌ возвращаем срез → underlying array на хипе\nfunc Read(n int) []byte {\n    buf := make([]byte, n)\n    \u002F\u002F ... fill buf ...\n    return buf  \u002F\u002F срез содержит указатель → хип\n}\n\n\u002F\u002F ✅ принимаем срез → вызывающий контролирует аллокацию\nfunc Read(buf []byte) int {\n    \u002F\u002F ... fill buf ...\n    return len(buf)  \u002F\u002F буфер мог быть на стеке вызывающего\n}\n",[130,2458,2459,2464,2488,2504,2509,2519,2523,2527,2532,2554,2558,2571],{"__ignoreMap":5},[133,2460,2461],{"class":135,"line":136},[133,2462,2463],{"class":164},"\u002F\u002F ❌ возвращаем срез → underlying array на хипе\n",[133,2465,2466,2468,2471,2473,2477,2480,2483,2486],{"class":135,"line":151},[133,2467,140],{"class":139},[133,2469,2470],{"class":143}," Read",[133,2472,818],{"class":147},[133,2474,2476],{"class":2475},"s9osk","n",[133,2478,2479],{"class":139}," int",[133,2481,2482],{"class":147},") []",[133,2484,2485],{"class":139},"byte",[133,2487,238],{"class":147},[133,2489,2490,2493,2495,2497,2499,2501],{"class":135,"line":168},[133,2491,2492],{"class":147},"    buf ",[133,2494,157],{"class":139},[133,2496,314],{"class":143},[133,2498,317],{"class":147},[133,2500,2485],{"class":139},[133,2502,2503],{"class":147},", n)\n",[133,2505,2506],{"class":135,"line":194},[133,2507,2508],{"class":164},"    \u002F\u002F ... fill buf ...\n",[133,2510,2511,2513,2516],{"class":135,"line":211},[133,2512,254],{"class":139},[133,2514,2515],{"class":147}," buf  ",[133,2517,2518],{"class":164},"\u002F\u002F срез содержит указатель → хип\n",[133,2520,2521],{"class":135,"line":217},[133,2522,214],{"class":147},[133,2524,2525],{"class":135,"line":224},[133,2526,221],{"emptyLinePlaceholder":220},[133,2528,2529],{"class":135,"line":241},[133,2530,2531],{"class":164},"\u002F\u002F ✅ принимаем срез → вызывающий контролирует аллокацию\n",[133,2533,2534,2536,2538,2540,2543,2545,2547,2550,2552],{"class":135,"line":251},[133,2535,140],{"class":139},[133,2537,2470],{"class":143},[133,2539,818],{"class":147},[133,2541,2542],{"class":2475},"buf",[133,2544,1284],{"class":147},[133,2546,2485],{"class":139},[133,2548,2549],{"class":147},") ",[133,2551,185],{"class":139},[133,2553,238],{"class":147},[133,2555,2556],{"class":135,"line":266},[133,2557,2508],{"class":164},[133,2559,2560,2562,2565,2568],{"class":135,"line":271},[133,2561,254],{"class":139},[133,2563,2564],{"class":143}," len",[133,2566,2567],{"class":147},"(buf)  ",[133,2569,2570],{"class":164},"\u002F\u002F буфер мог быть на стеке вызывающего\n",[133,2572,2573],{"class":135,"line":276},[133,2574,214],{"class":147},[594,2576,2578],{"id":2577},"переменная-в-цикле","Переменная в цикле",[123,2580,2582],{"className":125,"code":2581,"language":127,"meta":5,"style":5},"\u002F\u002F ❌ каждую итерацию: сдвиг SP + инициализация\nfor i := 0; i \u003C 1000000; i++ {\n    data := SomeStruct{}\n    init(&data)\n    process(&data)\n}\n\n\u002F\u002F ✅ одна аллокация, реинициализация\nvar data SomeStruct\nfor i := 0; i \u003C 1000000; i++ {\n    init(&data)\n    process(&data)\n}\n",[130,2583,2584,2589,2618,2631,2643,2654,2658,2662,2667,2677,2699,2709,2719],{"__ignoreMap":5},[133,2585,2586],{"class":135,"line":136},[133,2587,2588],{"class":164},"\u002F\u002F ❌ каждую итерацию: сдвиг SP + инициализация\n",[133,2590,2591,2594,2596,2598,2601,2604,2607,2610,2613,2616],{"class":135,"line":151},[133,2592,2593],{"class":139},"for",[133,2595,339],{"class":147},[133,2597,157],{"class":139},[133,2599,2600],{"class":160}," 0",[133,2602,2603],{"class":147},"; i ",[133,2605,2606],{"class":139},"\u003C",[133,2608,2609],{"class":160}," 1000000",[133,2611,2612],{"class":147},"; i",[133,2614,2615],{"class":139},"++",[133,2617,238],{"class":147},[133,2619,2620,2623,2625,2628],{"class":135,"line":168},[133,2621,2622],{"class":147},"    data ",[133,2624,157],{"class":139},[133,2626,2627],{"class":143}," SomeStruct",[133,2629,2630],{"class":147},"{}\n",[133,2632,2633,2636,2638,2640],{"class":135,"line":194},[133,2634,2635],{"class":143},"    init",[133,2637,818],{"class":147},[133,2639,1558],{"class":139},[133,2641,2642],{"class":147},"data)\n",[133,2644,2645,2648,2650,2652],{"class":135,"line":211},[133,2646,2647],{"class":143},"    process",[133,2649,818],{"class":147},[133,2651,1558],{"class":139},[133,2653,2642],{"class":147},[133,2655,2656],{"class":135,"line":217},[133,2657,214],{"class":147},[133,2659,2660],{"class":135,"line":224},[133,2661,221],{"emptyLinePlaceholder":220},[133,2663,2664],{"class":135,"line":241},[133,2665,2666],{"class":164},"\u002F\u002F ✅ одна аллокация, реинициализация\n",[133,2668,2669,2671,2674],{"class":135,"line":251},[133,2670,679],{"class":139},[133,2672,2673],{"class":147}," data ",[133,2675,2676],{"class":143},"SomeStruct\n",[133,2678,2679,2681,2683,2685,2687,2689,2691,2693,2695,2697],{"class":135,"line":266},[133,2680,2593],{"class":139},[133,2682,339],{"class":147},[133,2684,157],{"class":139},[133,2686,2600],{"class":160},[133,2688,2603],{"class":147},[133,2690,2606],{"class":139},[133,2692,2609],{"class":160},[133,2694,2612],{"class":147},[133,2696,2615],{"class":139},[133,2698,238],{"class":147},[133,2700,2701,2703,2705,2707],{"class":135,"line":271},[133,2702,2635],{"class":143},[133,2704,818],{"class":147},[133,2706,1558],{"class":139},[133,2708,2642],{"class":147},[133,2710,2711,2713,2715,2717],{"class":135,"line":276},[133,2712,2647],{"class":143},[133,2714,818],{"class":147},[133,2716,1558],{"class":139},[133,2718,2642],{"class":147},[133,2720,2721],{"class":135,"line":286},[133,2722,214],{"class":147},[117,2724,2725],{},"Даже на стеке: каждая «аллокация» = сдвинуть stack pointer + занулить память. В горячем цикле это заметно.",[594,2727,2729],{"id":2728},"особенность-escape-анализа-литерал-vs-точка","Особенность escape-анализа: литерал vs точка",[123,2731,2733],{"className":125,"code":2732,"language":127,"meta":5,"style":5},"type S struct { Pointer *int }\n\n\u002F\u002F ✅ стек — всё инициализируется в литерале\nnumber := 42\ns := S{Pointer: &number}\n\n\u002F\u002F ❌ хип — number escapes (особенность реализации анализа)\nnumber := 42\ns := S{}\ns.Pointer = &number  \u002F\u002F moved to heap: number\n",[130,2734,2735,2752,2756,2761,2770,2786,2790,2795,2803,2813],{"__ignoreMap":5},[133,2736,2737,2739,2742,2744,2747,2749],{"class":135,"line":136},[133,2738,1788],{"class":139},[133,2740,2741],{"class":143}," S",[133,2743,1794],{"class":139},[133,2745,2746],{"class":147}," { Pointer ",[133,2748,235],{"class":139},[133,2750,2751],{"class":147}," }\n",[133,2753,2754],{"class":135,"line":151},[133,2755,221],{"emptyLinePlaceholder":220},[133,2757,2758],{"class":135,"line":168},[133,2759,2760],{"class":164},"\u002F\u002F ✅ стек — всё инициализируется в литерале\n",[133,2762,2763,2766,2768],{"class":135,"line":194},[133,2764,2765],{"class":147},"number ",[133,2767,157],{"class":139},[133,2769,248],{"class":160},[133,2771,2772,2774,2776,2778,2781,2783],{"class":135,"line":211},[133,2773,697],{"class":147},[133,2775,157],{"class":139},[133,2777,2741],{"class":143},[133,2779,2780],{"class":147},"{Pointer: ",[133,2782,1558],{"class":139},[133,2784,2785],{"class":147},"number}\n",[133,2787,2788],{"class":135,"line":217},[133,2789,221],{"emptyLinePlaceholder":220},[133,2791,2792],{"class":135,"line":224},[133,2793,2794],{"class":164},"\u002F\u002F ❌ хип — number escapes (особенность реализации анализа)\n",[133,2796,2797,2799,2801],{"class":135,"line":241},[133,2798,2765],{"class":147},[133,2800,157],{"class":139},[133,2802,248],{"class":160},[133,2804,2805,2807,2809,2811],{"class":135,"line":251},[133,2806,697],{"class":147},[133,2808,157],{"class":139},[133,2810,2741],{"class":143},[133,2812,2630],{"class":147},[133,2814,2815,2818,2820,2822,2825],{"class":135,"line":266},[133,2816,2817],{"class":147},"s.Pointer ",[133,2819,200],{"class":139},[133,2821,257],{"class":139},[133,2823,2824],{"class":147},"number  ",[133,2826,2827],{"class":164},"\u002F\u002F moved to heap: number\n",[117,2829,2830],{},"Идентичный код, разный результат. Escape-анализ строит граф по-разному.",[117,2832,2833],{},"Правила могут меняться между версиями Go.",[594,2835,2837],{"id":2836},"главное-правило","Главное правило",[117,2839,2840,2841,2844],{},"Не оптимизируй без необходимости. Читаемый код > оптимизированный. Эти приёмы — ",[18,2842,2843],{},"только"," когда профилирование показало проблему с аллокациями.",[418,2846],{},[11,2848,2849,2855,2864],{},[14,2850,2851,2854],{},[18,2852,2853],{},"TCMalloc"," = Thread-Cache Malloc — аллокатор от Google",[14,2856,2857,2858,576,2861],{},"решает две проблемы обычного malloc: ",[18,2859,2860],{},"contention",[18,2862,2863],{},"фрагментация",[14,2865,2866],{},"Go взял обе идеи и улучшил: кэш на P вместо потока",[418,2868],{},[117,2870,2871],{},[18,2872,2873],{},"Проблема 1: Contention",[117,2875,2876],{},"malloc: все потоки конкурируют за один глобальный лок.",[117,2878,2879],{},"→ TCMalloc: у каждого потока свой кэш. Большинство аллокаций без лока.",[117,2881,2882],{},"→ Go: кэш на P (не на поток), чтобы не тратить память на заблокированных потоках.",[117,2884,2885],{},[18,2886,2887],{},"Проблема 2: Фрагментация",[117,2889,2890],{},"malloc: выделяет блоки произвольного размера → дыры разного размера → фрагментация.",[117,2892,2893],{},"→ TCMalloc: блоки фиксированных размеров (классы). Одинаковые рядом → нет фрагментации.",[117,2895,2896],{},[18,2897,2898],{},"Почему Go не вызывает malloc напрямую:",[117,2900,2901],{},"Каждый вызов malloc = syscall = дорого. Go запрашивает у ОС сразу 64MB (арена) и раздаёт сам.",[418,2903],{},[589,2905,2907],{"id":2906},"практика","Практика",[2909,2910,2913,2916,2940],"quiz",{"answer":1297,"id":2911,"xp":2912},"advanced-allocator-q1","10",[117,2914,2915],{},"Какой командой удобнее всего посмотреть решения escape analysis для конкретного пакета?",[2917,2918,2919],"template",{"v-slot:options":5},[11,2920,2921,2926,2930,2935],{},[14,2922,2923],{},[130,2924,2925],{},"go test -race",[14,2927,2928],{},[130,2929,415],{},[14,2931,2932],{},[130,2933,2934],{},"go fmt .\u002F...",[14,2936,2937],{},[130,2938,2939],{},"go env GOPATH",[2917,2941,2942],{"v-slot:explanation":5},[117,2943,2944,2946,2947,2950],{},[130,2945,415],{}," показывает, какие значения moved to heap, что inline-ится и почему компилятор принял такое решение. ",[130,2948,2949],{},"-l"," отключает inlining, чтобы отчёт было проще читать.",[2952,2953,2956,2959,3057],"predict",{"answer":2912,"id":2954,"xp":2955},"advanced-allocator-p1","15",[117,2957,2958],{},"Что выведет программа?",[2917,2960,2961],{"v-slot:code":5},[123,2962,2964],{"className":125,"code":2963,"language":127,"meta":5,"style":5},"package main\n\nimport \"fmt\"\n\nfunc makePtr() *int {\n    x := 10\n    return &x\n}\n\nfunc main() {\n    fmt.Println(*makePtr())\n}\n",[130,2965,2966,2972,2976,2986,2990,3003,3012,3021,3025,3029,3037,3053],{"__ignoreMap":5},[133,2967,2968,2970],{"class":135,"line":136},[133,2969,1176],{"class":139},[133,2971,1179],{"class":143},[133,2973,2974],{"class":135,"line":151},[133,2975,221],{"emptyLinePlaceholder":220},[133,2977,2978,2980,2982,2984],{"class":135,"line":168},[133,2979,1188],{"class":139},[133,2981,1191],{"class":929},[133,2983,1194],{"class":143},[133,2985,1197],{"class":929},[133,2987,2988],{"class":135,"line":194},[133,2989,221],{"emptyLinePlaceholder":220},[133,2991,2992,2994,2997,2999,3001],{"class":135,"line":211},[133,2993,140],{"class":139},[133,2995,2996],{"class":143}," makePtr",[133,2998,232],{"class":147},[133,3000,235],{"class":139},[133,3002,238],{"class":147},[133,3004,3005,3007,3009],{"class":135,"line":217},[133,3006,154],{"class":147},[133,3008,157],{"class":139},[133,3010,3011],{"class":160}," 10\n",[133,3013,3014,3016,3018],{"class":135,"line":224},[133,3015,254],{"class":139},[133,3017,257],{"class":139},[133,3019,3020],{"class":147},"x\n",[133,3022,3023],{"class":135,"line":241},[133,3024,214],{"class":147},[133,3026,3027],{"class":135,"line":251},[133,3028,221],{"emptyLinePlaceholder":220},[133,3030,3031,3033,3035],{"class":135,"line":266},[133,3032,140],{"class":139},[133,3034,1208],{"class":143},[133,3036,148],{"class":147},[133,3038,3039,3041,3043,3045,3047,3050],{"class":135,"line":271},[133,3040,1246],{"class":147},[133,3042,776],{"class":143},[133,3044,818],{"class":147},[133,3046,2369],{"class":139},[133,3048,3049],{"class":143},"makePtr",[133,3051,3052],{"class":147},"())\n",[133,3054,3055],{"class":135,"line":276},[133,3056,214],{"class":147},[2917,3058,3059],{"v-slot:hint":5},[117,3060,3061,3062,3064,3065,3068,3069,3071],{},"Возврат ",[130,3063,607],{}," заставляет ",[130,3066,3067],{},"x"," жить дольше stack frame функции ",[130,3070,3049],{},", поэтому компилятор перенесёт значение в heap. На вывод это не влияет.",[3073,3074,3078,3085,3263],"code-task",{"expected":3075,"id":3076,"xp":3077},"3\\ntrue","advanced-allocator-ct1","20",[117,3079,3080,3081,3084],{},"Реализуй ",[130,3082,3083],{},"Collect",": функция должна дописать значения в переданный буфер и вернуть результат, переиспользуя уже выделенную capacity.",[2917,3086,3087],{"v-slot:template":5},[123,3088,3090],{"className":125,"code":3089,"language":127,"meta":5,"style":5},"package main\n\nimport \"fmt\"\n\nfunc Collect(dst []int, values ...int) []int {\n    return nil\n}\n\nfunc main() {\n    buf := make([]int, 0, 4)\n    out := Collect(buf, 1, 2, 3)\n\n    fmt.Println(len(out))\n    fmt.Println(cap(out) == cap(buf))\n}\n",[130,3091,3092,3098,3102,3112,3116,3146,3153,3157,3161,3169,3194,3218,3222,3236,3259],{"__ignoreMap":5},[133,3093,3094,3096],{"class":135,"line":136},[133,3095,1176],{"class":139},[133,3097,1179],{"class":143},[133,3099,3100],{"class":135,"line":151},[133,3101,221],{"emptyLinePlaceholder":220},[133,3103,3104,3106,3108,3110],{"class":135,"line":168},[133,3105,1188],{"class":139},[133,3107,1191],{"class":929},[133,3109,1194],{"class":143},[133,3111,1197],{"class":929},[133,3113,3114],{"class":135,"line":194},[133,3115,221],{"emptyLinePlaceholder":220},[133,3117,3118,3120,3123,3125,3128,3130,3132,3134,3137,3140,3142,3144],{"class":135,"line":211},[133,3119,140],{"class":139},[133,3121,3122],{"class":143}," Collect",[133,3124,818],{"class":147},[133,3126,3127],{"class":2475},"dst",[133,3129,1284],{"class":147},[133,3131,185],{"class":139},[133,3133,708],{"class":147},[133,3135,3136],{"class":2475},"values",[133,3138,3139],{"class":139}," ...int",[133,3141,2482],{"class":147},[133,3143,185],{"class":139},[133,3145,238],{"class":147},[133,3147,3148,3150],{"class":135,"line":217},[133,3149,254],{"class":139},[133,3151,3152],{"class":160}," nil\n",[133,3154,3155],{"class":135,"line":224},[133,3156,214],{"class":147},[133,3158,3159],{"class":135,"line":241},[133,3160,221],{"emptyLinePlaceholder":220},[133,3162,3163,3165,3167],{"class":135,"line":251},[133,3164,140],{"class":139},[133,3166,1208],{"class":143},[133,3168,148],{"class":147},[133,3170,3171,3173,3175,3177,3179,3181,3183,3186,3188,3191],{"class":135,"line":266},[133,3172,2492],{"class":147},[133,3174,157],{"class":139},[133,3176,314],{"class":143},[133,3178,317],{"class":147},[133,3180,185],{"class":139},[133,3182,708],{"class":147},[133,3184,3185],{"class":160},"0",[133,3187,708],{"class":147},[133,3189,3190],{"class":160},"4",[133,3192,3193],{"class":147},")\n",[133,3195,3196,3199,3201,3203,3206,3208,3210,3212,3214,3216],{"class":135,"line":271},[133,3197,3198],{"class":147},"    out ",[133,3200,157],{"class":139},[133,3202,3122],{"class":143},[133,3204,3205],{"class":147},"(buf, ",[133,3207,1292],{"class":160},[133,3209,708],{"class":147},[133,3211,1297],{"class":160},[133,3213,708],{"class":147},[133,3215,1302],{"class":160},[133,3217,3193],{"class":147},[133,3219,3220],{"class":135,"line":276},[133,3221,221],{"emptyLinePlaceholder":220},[133,3223,3224,3226,3228,3230,3233],{"class":135,"line":286},[133,3225,1246],{"class":147},[133,3227,776],{"class":143},[133,3229,818],{"class":147},[133,3231,3232],{"class":143},"len",[133,3234,3235],{"class":147},"(out))\n",[133,3237,3238,3240,3242,3244,3247,3250,3253,3256],{"class":135,"line":297},[133,3239,1246],{"class":147},[133,3241,776],{"class":143},[133,3243,818],{"class":147},[133,3245,3246],{"class":143},"cap",[133,3248,3249],{"class":147},"(out) ",[133,3251,3252],{"class":139},"==",[133,3254,3255],{"class":143}," cap",[133,3257,3258],{"class":147},"(buf))\n",[133,3260,3261],{"class":135,"line":306},[133,3262,214],{"class":147},[2917,3264,3265],{"v-slot:hints":5},[11,3266,3267,3274,3280],{},[14,3268,3269,3270,3273],{},"Начни с ",[130,3271,3272],{},"dst = dst[:0]",", если хочешь переиспользовать буфер как пустой",[14,3275,3276,3277],{},"Используй ",[130,3278,3279],{},"append(dst, values...)",[14,3281,3282,3283,3286],{},"Если capacity хватает, ",[130,3284,3285],{},"append"," не выделит новый backing array",[3288,3289,3290],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":5,"searchDepth":151,"depth":151,"links":3292},[3293,3300,3307,3314],{"id":591,"depth":151,"text":592,"children":3294},[3295,3296,3297,3298,3299],{"id":596,"depth":168,"text":597},{"id":614,"depth":168,"text":615},{"id":727,"depth":168,"text":728},{"id":899,"depth":168,"text":900},{"id":914,"depth":168,"text":915},{"id":1756,"depth":151,"text":1757,"children":3301},[3302,3303,3304,3305,3306],{"id":1760,"depth":168,"text":1761},{"id":1738,"depth":168,"text":1773},{"id":1854,"depth":168,"text":1855},{"id":1969,"depth":168,"text":1970},{"id":2079,"depth":168,"text":2080},{"id":2342,"depth":151,"text":2343,"children":3308},[3309,3310,3311,3312,3313],{"id":2346,"depth":168,"text":2347},{"id":2452,"depth":168,"text":2453},{"id":2577,"depth":168,"text":2578},{"id":2728,"depth":168,"text":2729},{"id":2836,"depth":168,"text":2837},{"id":2906,"depth":151,"text":2907},1781458320140]