[{"data":1,"prerenderedAt":3860},["ShallowReactive",2],{"navigation":3,"\u002Fblog\u002Fescape-analysis-em-go-stack-ou-heap":26},[4],{"title":5,"path":6,"stem":7,"children":8,"page":25},"Blog","\u002Fblog","blog",[9,13,17,21],{"title":10,"path":11,"stem":12},"Array ou slice? Capacidade, crescimento e as armadilhas do append","\u002Fblog\u002Farrays-e-slices-em-go-capacidade-crescimento-e-append","blog\u002Farrays-e-slices-em-go-capacidade-crescimento-e-append",{"title":14,"path":15,"stem":16},"Stack ou heap? Entendendo o escape analysis do Go","\u002Fblog\u002Fescape-analysis-em-go-stack-ou-heap","blog\u002Fescape-analysis-em-go-stack-ou-heap",{"title":18,"path":19,"stem":20},"Por que Go copia tudo? Zero Values e Pointer Semantics","\u002Fblog\u002Fgo-zero-values-e-value-semantics-vs-pointer-semantics","blog\u002Fgo-zero-values-e-value-semantics-vs-pointer-semantics",{"title":22,"path":23,"stem":24},"Nil map ou map vazio? Zero value, ordem de iteração e concorrência em Go","\u002Fblog\u002Fmaps-em-go-zero-value-ordem-de-iteracao-e-concorrencia","blog\u002Fmaps-em-go-zero-value-ordem-de-iteracao-e-concorrencia",false,{"id":27,"title":14,"author":28,"body":29,"date":3853,"description":3854,"extension":3855,"image":3856,"meta":3857,"minRead":881,"navigation":372,"path":15,"seo":3858,"stem":16,"tags":28,"updatedAt":28,"__hash__":3859},"blog\u002Fblog\u002Fescape-analysis-em-go-stack-ou-heap.md",null,{"type":30,"value":31,"toc":3833},"minimark",[32,42,45,131,134,191,199,202,216,221,224,235,242,248,258,262,281,284,302,305,311,318,321,335,348,355,553,561,575,582,589,611,614,937,940,946,956,959,1017,1036,1041,1047,1388,1391,1397,1400,1453,1472,1476,1479,1483,1494,1501,1680,1686,1701,1707,1720,1724,1734,1737,1970,1984,1990,2033,2052,2059,2310,2316,2345,2362,2367,2371,2374,2387,2394,2398,2401,2670,2676,2690,2703,2722,2726,2736,2739,2783,2987,3000,3006,3019,3023,3029,3032,3044,3074,3080,3130,3136,3151,3164,3167,3171,3174,3186,3421,3427,3440,3448,3458,3471,3480,3493,3505,3509,3512,3570,3577,3581,3584,3650,3656,3660,3829],[33,34,35,36,41],"p",{},"No artigo sobre ",[37,38,40],"a",{"href":39},"\u002Fgo-zero-values-e-value-semantics-vs-pointer-semantics","zero values e pointer semantics"," eu terminei falando que, em Go, tudo é passado por valor, inclusive ponteiros. Ficou faltando responder uma pergunta que aparece logo em seguida: se eu pego o endereço de uma variável local e devolvo esse endereço, o que impede o dado de sumir quando a função retorna?",[33,43,44],{},"Em C, isso é um bug clássico:",[46,47,52],"pre",{"className":48,"code":49,"language":50,"meta":51,"style":51},"language-c shiki shiki-themes material-theme-lighter github-light github-dark","int *broken(void) {\n    int x = 42;\n    return &x; \u002F\u002F ponteiro pendurado: x morre com o frame\n}\n","c","",[53,54,55,85,105,125],"code",{"__ignoreMap":51},[56,57,60,64,68,72,76,79,82],"span",{"class":58,"line":59},"line",1,[56,61,63],{"class":62},"sbsja","int",[56,65,67],{"class":66},"smGrS"," *",[56,69,71],{"class":70},"sGLFI","broken",[56,73,75],{"class":74},"sP7_E","(",[56,77,78],{"class":62},"void",[56,80,81],{"class":74},")",[56,83,84],{"class":74}," {\n",[56,86,88,91,95,98,102],{"class":58,"line":87},2,[56,89,90],{"class":62},"    int",[56,92,94],{"class":93},"skxfh"," x ",[56,96,97],{"class":66},"=",[56,99,101],{"class":100},"srdBf"," 42",[56,103,104],{"class":74},";\n",[56,106,108,112,115,118,121],{"class":58,"line":107},3,[56,109,111],{"class":110},"sVHd0","    return",[56,113,114],{"class":66}," &",[56,116,117],{"class":93},"x",[56,119,120],{"class":74},";",[56,122,124],{"class":123},"sutJx"," \u002F\u002F ponteiro pendurado: x morre com o frame\n",[56,126,128],{"class":58,"line":127},4,[56,129,130],{"class":74},"}\n",[33,132,133],{},"Em Go, o equivalente é código idiomático e aparece em praticamente todo construtor que você já escreveu:",[46,135,139],{"className":136,"code":137,"language":138,"meta":51,"style":51},"language-go shiki shiki-themes material-theme-lighter github-light github-dark","func NewCounter() *Counter {\n    c := Counter{}\n    return &c \u002F\u002F isso é normal em Go\n}\n","go",[53,140,141,160,175,187],{"__ignoreMap":51},[56,142,143,146,149,152,154,158],{"class":58,"line":59},[56,144,145],{"class":66},"func",[56,147,148],{"class":70}," NewCounter",[56,150,151],{"class":74},"()",[56,153,67],{"class":66},[56,155,157],{"class":156},"sbgvK","Counter",[56,159,84],{"class":74},[56,161,162,166,169,172],{"class":58,"line":87},[56,163,165],{"class":164},"su5hD","    c ",[56,167,168],{"class":66},":=",[56,170,171],{"class":156}," Counter",[56,173,174],{"class":74},"{}\n",[56,176,177,179,181,184],{"class":58,"line":107},[56,178,111],{"class":110},[56,180,114],{"class":66},[56,182,183],{"class":164},"c ",[56,185,186],{"class":123},"\u002F\u002F isso é normal em Go\n",[56,188,189],{"class":58,"line":127},[56,190,130],{"class":74},[33,192,193,194,198],{},"Os dois programas parecem fazer a mesma coisa, mas só um deles funciona. A diferença é uma etapa do compilador chamada ",[195,196,197],"strong",{},"escape analysis",", e ela é o que permite que Go tenha ponteiros sem ter as armadilhas de ponteiros.",[33,200,201],{},"A proposta deste artigo é ir do porquê ao como: primeiro por que stack e heap custam diferente, depois como o compilador decide entre as duas, como enxergar essa decisão com suas próprias ferramentas, quais padrões de código realmente causam escape e, no fim, o que fazer (e o que não fazer) com essa informação.",[203,204,205],"note",{},[33,206,207,208,211,212,215],{},"Tudo aqui foi verificado com ",[195,209,210],{},"Go 1.26"," no ",[53,213,214],{},"gc"," (o compilador oficial). Números concretos, como limites de tamanho, são detalhes de implementação e já mudaram entre versões; existe uma seção sobre isso mais adiante.",[217,218,220],"h2",{"id":219},"duas-regiões-de-memória-dois-custos-bem-diferentes","Duas regiões de memória, dois custos bem diferentes",[33,222,223],{},"Antes de falar da decisão, vale entender por que ela importa.",[33,225,226,227,230,231,234],{},"A ",[195,228,229],{},"stack"," é uma região por goroutine. Ela funciona em LIFO: chamou função, empilhou um frame; retornou, desempilhou. Alocar espaço na stack é somar um número a um registrador, e liberar é subtrair. Não existe fragmentação, não existe busca por espaço livre e, principalmente, ",[195,232,233],{},"o coletor de lixo não precisa saber nada sobre isso",". Quando a função retorna, a memória já era.",[33,236,237,238,241],{},"O ",[195,239,240],{},"heap"," é uma região compartilhada por todo o processo. Ele existe justamente para dados cujo tempo de vida não se encaixa no ciclo de vida de uma chamada de função. O preço é que alguém precisa descobrir quando aquele dado pode ser liberado, e esse alguém é o garbage collector. Cada objeto no heap é trabalho de marcação e varredura para o GC.",[33,243,244,245],{},"Então a conta é essa: ",[195,246,247],{},"stack é barata na alocação e de graça no GC. Heap é mais cara na alocação e, pior, continua custando depois.",[249,250,251],"tip",{},[33,252,253,254,257],{},"Um detalhe que costuma surpreender: as stacks das goroutines em Go não têm tamanho fixo. Cada goroutine começa com uma stack pequena (2 KiB, definidos pela constante ",[53,255,256],{},"stackMin"," no runtime) e o runtime a faz crescer copiando para uma área maior quando necessário, até um teto de 1 GB em plataformas de 64 bits. É por isso que criar centenas de milhares de goroutines é viável.",[217,259,261],{"id":260},"parte-1-quem-decide-é-o-compilador-não-você","Parte 1: quem decide é o compilador, não você",[33,263,264,265,268,269,272,273,276,277,280],{},"Aqui está o ponto que reorganiza o entendimento de quem vem de C ou C++: em Go, ",[195,266,267],{},"não existe sintaxe para escolher entre stack e heap",". ",[53,270,271],{},"new"," não significa heap. ",[53,274,275],{},"&T{}"," não significa heap. Uma variável sem ",[53,278,279],{},"&"," não significa stack.",[33,282,283],{},"A própria FAQ oficial é direta sobre isso:",[203,285,286,289],{},[33,287,288],{},"\"Do ponto de vista da correção, você não precisa saber. Cada variável em Go existe enquanto houver referências a ela. O local de armazenamento escolhido pela implementação é irrelevante para a semântica da linguagem.\"",[33,290,291,292],{},"— ",[37,293,297,298],{"href":294,"rel":295},"https:\u002F\u002Fgo.dev\u002Fdoc\u002Ffaq#stack_or_heap",[296],"nofollow","Go FAQ, ",[299,300,301],"em",{},"How do I know whether a variable is allocated on the heap or the stack?",[33,303,304],{},"E o mesmo texto explica o critério:",[306,307,308],"blockquote",{},[33,309,310],{},"\"se o compilador não conseguir provar que a variável não é referenciada depois que a função retorna, então o compilador precisa alocar a variável no heap garantido pelo coletor de lixo, para evitar erros de ponteiro pendurado.\"",[33,312,313,314,317],{},"Repare na formulação: o compilador não procura motivos para mandar algo para o heap. Ele tenta ",[195,315,316],{},"provar"," que pode manter na stack, e o heap é o que sobra quando a prova falha. É uma diferença importante, e ela explica boa parte do comportamento que parece estranho depois.",[33,319,320],{},"O comentário no topo do código-fonte do analisador enuncia as duas regras que sustentam tudo:",[306,322,323,326],{},[33,324,325],{},"\"(1) ponteiros para objetos na stack não podem ser guardados no heap e (2) ponteiros para um objeto na stack não podem sobreviver a esse objeto\"",[33,327,291,328],{},[37,329,332],{"href":330,"rel":331},"https:\u002F\u002Fgithub.com\u002Fgolang\u002Fgo\u002Fblob\u002Fmaster\u002Fsrc\u002Fcmd\u002Fcompile\u002Finternal\u002Fescape\u002Fescape.go",[296],[53,333,334],{},"cmd\u002Fcompile\u002Finternal\u002Fescape\u002Fescape.go",[33,336,337,338,341,342,344,345,347],{},"Voltando ao ",[53,339,340],{},"NewCounter",": o ponteiro devolvido sobrevive ao frame, logo a regra (2) seria violada se ",[53,343,50],{}," ficasse na stack. O compilador então move ",[53,346,50],{}," para o heap. O programa continua correto sem você fazer nada, e é exatamente esse \"sem você fazer nada\" que o C não oferece.",[33,349,350,351,354],{},"Uma consequência prática que quase ninguém aproveita: ",[53,352,353],{},"new(T)"," pode perfeitamente ficar na stack.",[46,356,358],{"className":136,"code":357,"language":138,"meta":51,"style":51},"package cfg\n\ntype Config struct {\n    Retries int\n    Timeout int\n}\n\n\u002F\u002Fgo:noinline\nfunc validate(c *Config) bool { return c.Retries > 0 }\n\nfunc Load() bool {\n    c := new(Config) \u002F\u002F \"new\" não quer dizer heap\n    c.Retries = 3\n    c.Timeout = 5\n    return validate(c)\n}\n",[53,359,360,368,374,387,395,403,408,413,419,466,471,485,504,519,534,548],{"__ignoreMap":51},[56,361,362,365],{"class":58,"line":59},[56,363,364],{"class":66},"package",[56,366,367],{"class":156}," cfg\n",[56,369,370],{"class":58,"line":87},[56,371,373],{"emptyLinePlaceholder":372},true,"\n",[56,375,376,379,382,385],{"class":58,"line":107},[56,377,378],{"class":66},"type",[56,380,381],{"class":156}," Config",[56,383,384],{"class":66}," struct",[56,386,84],{"class":74},[56,388,389,392],{"class":58,"line":127},[56,390,391],{"class":164},"    Retries ",[56,393,394],{"class":62},"int\n",[56,396,398,401],{"class":58,"line":397},5,[56,399,400],{"class":164},"    Timeout ",[56,402,394],{"class":62},[56,404,406],{"class":58,"line":405},6,[56,407,130],{"class":74},[56,409,411],{"class":58,"line":410},7,[56,412,373],{"emptyLinePlaceholder":372},[56,414,416],{"class":58,"line":415},8,[56,417,418],{"class":123},"\u002F\u002Fgo:noinline\n",[56,420,422,424,427,429,432,434,437,439,442,445,448,451,454,457,460,463],{"class":58,"line":421},9,[56,423,145],{"class":66},[56,425,426],{"class":70}," validate",[56,428,75],{"class":74},[56,430,50],{"class":431},"s99_P",[56,433,67],{"class":66},[56,435,436],{"class":156},"Config",[56,438,81],{"class":74},[56,440,441],{"class":62}," bool",[56,443,444],{"class":74}," {",[56,446,447],{"class":110}," return",[56,449,450],{"class":164}," c",[56,452,453],{"class":74},".",[56,455,456],{"class":164},"Retries ",[56,458,459],{"class":66},">",[56,461,462],{"class":100}," 0",[56,464,465],{"class":74}," }\n",[56,467,469],{"class":58,"line":468},10,[56,470,373],{"emptyLinePlaceholder":372},[56,472,474,476,479,481,483],{"class":58,"line":473},11,[56,475,145],{"class":66},[56,477,478],{"class":70}," Load",[56,480,151],{"class":74},[56,482,441],{"class":62},[56,484,84],{"class":74},[56,486,488,490,492,495,497,499,501],{"class":58,"line":487},12,[56,489,165],{"class":164},[56,491,168],{"class":66},[56,493,494],{"class":70}," new",[56,496,75],{"class":74},[56,498,436],{"class":156},[56,500,81],{"class":74},[56,502,503],{"class":123}," \u002F\u002F \"new\" não quer dizer heap\n",[56,505,507,510,512,514,516],{"class":58,"line":506},13,[56,508,509],{"class":164},"    c",[56,511,453],{"class":74},[56,513,456],{"class":164},[56,515,97],{"class":66},[56,517,518],{"class":100}," 3\n",[56,520,522,524,526,529,531],{"class":58,"line":521},14,[56,523,509],{"class":164},[56,525,453],{"class":74},[56,527,528],{"class":164},"Timeout ",[56,530,97],{"class":66},[56,532,533],{"class":100}," 5\n",[56,535,537,539,541,543,545],{"class":58,"line":536},15,[56,538,111],{"class":110},[56,540,426],{"class":70},[56,542,75],{"class":74},[56,544,50],{"class":164},[56,546,547],{"class":74},")\n",[56,549,551],{"class":58,"line":550},16,[56,552,130],{"class":74},[46,554,559],{"className":555,"code":557,"language":558,"meta":51},[556],"language-text",".\u002Fcfg.go:9:15:  c does not escape\n.\u002Fcfg.go:12:10: new(Config) does not escape\n","text",[53,560,557],{"__ignoreMap":51},[33,562,563,564,567,568,571,572,574],{},"O ponteiro entra em ",[53,565,566],{},"validate",", é lido e morre ali. Nada sobrevive ao frame de ",[53,569,570],{},"Load",", então a prova passa e o ",[53,573,271],{}," fica na stack.",[217,576,578,579],{"id":577},"parte-2-enxergando-a-decisão-com-gcflags-m","Parte 2: enxergando a decisão com ",[53,580,581],{},"-gcflags=-m",[33,583,584,585,588],{},"Você não precisa adivinhar nada disso. A flag ",[53,586,587],{},"-m"," pede ao compilador que imprima as decisões de otimização:",[46,590,594],{"className":591,"code":592,"language":593,"meta":51,"style":51},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark","go build -gcflags=-m .\u002F...\n","bash",[53,595,596],{"__ignoreMap":51},[56,597,598,600,604,608],{"class":58,"line":59},[56,599,138],{"class":156},[56,601,603],{"class":602},"s_sjI"," build",[56,605,607],{"class":606},"stzsN"," -gcflags=-m",[56,609,610],{"class":602}," .\u002F...\n",[33,612,613],{},"Vamos a um exemplo com três variações do mesmo cálculo:",[46,615,617],{"className":136,"code":616,"language":138,"meta":51,"style":51},"package geo\n\ntype Point struct {\n    X, Y int\n}\n\n\u002F\u002F 1. valor local, sem ponteiro\nfunc DistanceSquared(x, y int) int {\n    p := Point{X: x, Y: y}\n    return p.X*p.X + p.Y*p.Y\n}\n\n\u002F\u002F 2. pega o endereço, mas o ponteiro morre aqui\nfunc DistanceSquaredPtr(x, y int) int {\n    p := &Point{X: x, Y: y}\n    return p.X*p.X + p.Y*p.Y\n}\n\n\u002F\u002F 3. o ponteiro sai da função\nfunc NewPoint(x, y int) *Point {\n    return &Point{X: x, Y: y}\n}\n",[53,618,619,626,630,641,654,658,662,667,692,724,764,768,772,777,800,829,863,868,873,879,905,932],{"__ignoreMap":51},[56,620,621,623],{"class":58,"line":59},[56,622,364],{"class":66},[56,624,625],{"class":156}," geo\n",[56,627,628],{"class":58,"line":87},[56,629,373],{"emptyLinePlaceholder":372},[56,631,632,634,637,639],{"class":58,"line":107},[56,633,378],{"class":66},[56,635,636],{"class":156}," Point",[56,638,384],{"class":66},[56,640,84],{"class":74},[56,642,643,646,649,652],{"class":58,"line":127},[56,644,645],{"class":164},"    X",[56,647,648],{"class":74},",",[56,650,651],{"class":164}," Y ",[56,653,394],{"class":62},[56,655,656],{"class":58,"line":397},[56,657,130],{"class":74},[56,659,660],{"class":58,"line":405},[56,661,373],{"emptyLinePlaceholder":372},[56,663,664],{"class":58,"line":410},[56,665,666],{"class":123},"\u002F\u002F 1. valor local, sem ponteiro\n",[56,668,669,671,674,676,678,680,683,686,688,690],{"class":58,"line":415},[56,670,145],{"class":66},[56,672,673],{"class":70}," DistanceSquared",[56,675,75],{"class":74},[56,677,117],{"class":431},[56,679,648],{"class":74},[56,681,682],{"class":431}," y",[56,684,685],{"class":62}," int",[56,687,81],{"class":74},[56,689,685],{"class":62},[56,691,84],{"class":74},[56,693,694,697,699,701,704,707,710,713,715,718,720,722],{"class":58,"line":421},[56,695,696],{"class":164},"    p ",[56,698,168],{"class":66},[56,700,636],{"class":156},[56,702,703],{"class":74},"{",[56,705,706],{"class":164},"X",[56,708,709],{"class":74},":",[56,711,712],{"class":164}," x",[56,714,648],{"class":74},[56,716,717],{"class":164}," Y",[56,719,709],{"class":74},[56,721,682],{"class":164},[56,723,130],{"class":74},[56,725,726,728,731,733,735,738,740,742,745,748,750,752,755,757,759,761],{"class":58,"line":468},[56,727,111],{"class":110},[56,729,730],{"class":164}," p",[56,732,453],{"class":74},[56,734,706],{"class":164},[56,736,737],{"class":66},"*",[56,739,33],{"class":164},[56,741,453],{"class":74},[56,743,744],{"class":164},"X ",[56,746,747],{"class":66},"+",[56,749,730],{"class":164},[56,751,453],{"class":74},[56,753,754],{"class":164},"Y",[56,756,737],{"class":66},[56,758,33],{"class":164},[56,760,453],{"class":74},[56,762,763],{"class":164},"Y\n",[56,765,766],{"class":58,"line":473},[56,767,130],{"class":74},[56,769,770],{"class":58,"line":487},[56,771,373],{"emptyLinePlaceholder":372},[56,773,774],{"class":58,"line":506},[56,775,776],{"class":123},"\u002F\u002F 2. pega o endereço, mas o ponteiro morre aqui\n",[56,778,779,781,784,786,788,790,792,794,796,798],{"class":58,"line":521},[56,780,145],{"class":66},[56,782,783],{"class":70}," DistanceSquaredPtr",[56,785,75],{"class":74},[56,787,117],{"class":431},[56,789,648],{"class":74},[56,791,682],{"class":431},[56,793,685],{"class":62},[56,795,81],{"class":74},[56,797,685],{"class":62},[56,799,84],{"class":74},[56,801,802,804,806,808,811,813,815,817,819,821,823,825,827],{"class":58,"line":536},[56,803,696],{"class":164},[56,805,168],{"class":66},[56,807,114],{"class":66},[56,809,810],{"class":156},"Point",[56,812,703],{"class":74},[56,814,706],{"class":164},[56,816,709],{"class":74},[56,818,712],{"class":164},[56,820,648],{"class":74},[56,822,717],{"class":164},[56,824,709],{"class":74},[56,826,682],{"class":164},[56,828,130],{"class":74},[56,830,831,833,835,837,839,841,843,845,847,849,851,853,855,857,859,861],{"class":58,"line":550},[56,832,111],{"class":110},[56,834,730],{"class":164},[56,836,453],{"class":74},[56,838,706],{"class":164},[56,840,737],{"class":66},[56,842,33],{"class":164},[56,844,453],{"class":74},[56,846,744],{"class":164},[56,848,747],{"class":66},[56,850,730],{"class":164},[56,852,453],{"class":74},[56,854,754],{"class":164},[56,856,737],{"class":66},[56,858,33],{"class":164},[56,860,453],{"class":74},[56,862,763],{"class":164},[56,864,866],{"class":58,"line":865},17,[56,867,130],{"class":74},[56,869,871],{"class":58,"line":870},18,[56,872,373],{"emptyLinePlaceholder":372},[56,874,876],{"class":58,"line":875},19,[56,877,878],{"class":123},"\u002F\u002F 3. o ponteiro sai da função\n",[56,880,882,884,887,889,891,893,895,897,899,901,903],{"class":58,"line":881},20,[56,883,145],{"class":66},[56,885,886],{"class":70}," NewPoint",[56,888,75],{"class":74},[56,890,117],{"class":431},[56,892,648],{"class":74},[56,894,682],{"class":431},[56,896,685],{"class":62},[56,898,81],{"class":74},[56,900,67],{"class":66},[56,902,810],{"class":156},[56,904,84],{"class":74},[56,906,908,910,912,914,916,918,920,922,924,926,928,930],{"class":58,"line":907},21,[56,909,111],{"class":110},[56,911,114],{"class":66},[56,913,810],{"class":156},[56,915,703],{"class":74},[56,917,706],{"class":164},[56,919,709],{"class":74},[56,921,712],{"class":164},[56,923,648],{"class":74},[56,925,717],{"class":164},[56,927,709],{"class":74},[56,929,682],{"class":164},[56,931,130],{"class":74},[56,933,935],{"class":58,"line":934},22,[56,936,130],{"class":74},[33,938,939],{},"A saída relevante:",[46,941,944],{"className":942,"code":943,"language":558,"meta":51},[556],".\u002Fgeo.go:15:7: &Point{...} does not escape\n.\u002Fgeo.go:21:9: &Point{...} escapes to heap\n",[53,945,943],{"__ignoreMap":51},[33,947,948,949,955],{},"O caso 1 nem aparece: sem ponteiro, não há o que analisar. O caso 2 mostra a parte contraintuitiva: ",[195,950,951,952,954],{},"usar ",[53,953,279],{}," não manda nada para o heap",". O endereço nasce e morre dentro do frame, então a prova passa. Só o caso 3 escapa, porque o endereço atravessa a fronteira da função.",[33,957,958],{},"Vale conhecer as três formas de usar a flag:",[960,961,962,975],"table",{},[963,964,965],"thead",{},[966,967,968,972],"tr",{},[969,970,971],"th",{},"Comando",[969,973,974],{},"Para que serve",[976,977,978,989,1003],"tbody",{},[966,979,980,986],{},[981,982,983],"td",{},[53,984,985],{},"go build -gcflags=-m",[981,987,988],{},"decisões do pacote que você listou",[966,990,991,996],{},[981,992,993],{},[53,994,995],{},"go build -gcflags=-m=2",[981,997,998,999,1002],{},"acrescenta o ",[299,1000,1001],{},"motivo"," de cada escape, seguindo o caminho no grafo",[966,1004,1005,1010],{},[981,1006,1007],{},[53,1008,1009],{},"go build -gcflags=all=-m",[981,1011,1012,1013,1016],{},"inclui as dependências; ",[53,1014,1015],{},"all="," é um padrão de pacotes, não um modificador da flag",[249,1018,1019],{},[33,1020,1021,1022,1025,1026,1029,1030,1033,1034,453],{},"O prefixo ",[53,1023,1024],{},"pattern="," do ",[53,1027,1028],{},"-gcflags"," é documentado em ",[53,1031,1032],{},"go help build",": sem ele, os argumentos valem apenas para os pacotes nomeados na linha de comando. Se você quiser ver o que acontece dentro de uma biblioteca de terceiros, precisa de ",[53,1035,1015],{},[1037,1038,1040],"h3",{"id":1039},"lendo-o-vocabulário-da-saída","Lendo o vocabulário da saída",[33,1042,1043,1044,1046],{},"A saída do ",[53,1045,587],{}," tem um jargão próprio, e ele diz mais do que \"escapou ou não\". Considere um trecho realista de uma camada de repositório:",[46,1048,1050],{"className":136,"code":1049,"language":138,"meta":51,"style":51},"package orders\n\ntype Order struct {\n    ID     int64\n    Total  int64\n    Status string\n}\n\n\u002F\u002F só lê o parâmetro\nfunc isPaid(o *Order) bool {\n    return o.Status == \"paid\"\n}\n\n\u002F\u002F devolve o parâmetro\nfunc FirstPaid(orders []*Order) *Order {\n    for _, o := range orders {\n        if isPaid(o) {\n            return o\n        }\n    }\n    return nil\n}\n\ntype Registry struct {\n    byID map[int64]*Order\n}\n\n\u002F\u002F guarda o parâmetro em uma estrutura de vida longa\nfunc (r *Registry) Add(o *Order) {\n    r.byID[o.ID] = o\n}\n",[53,1051,1052,1059,1063,1074,1082,1089,1097,1101,1105,1110,1133,1158,1162,1166,1171,1198,1222,1237,1245,1250,1255,1263,1267,1272,1284,1307,1312,1317,1323,1356,1383],{"__ignoreMap":51},[56,1053,1054,1056],{"class":58,"line":59},[56,1055,364],{"class":66},[56,1057,1058],{"class":156}," orders\n",[56,1060,1061],{"class":58,"line":87},[56,1062,373],{"emptyLinePlaceholder":372},[56,1064,1065,1067,1070,1072],{"class":58,"line":107},[56,1066,378],{"class":66},[56,1068,1069],{"class":156}," Order",[56,1071,384],{"class":66},[56,1073,84],{"class":74},[56,1075,1076,1079],{"class":58,"line":127},[56,1077,1078],{"class":164},"    ID     ",[56,1080,1081],{"class":62},"int64\n",[56,1083,1084,1087],{"class":58,"line":397},[56,1085,1086],{"class":164},"    Total  ",[56,1088,1081],{"class":62},[56,1090,1091,1094],{"class":58,"line":405},[56,1092,1093],{"class":164},"    Status ",[56,1095,1096],{"class":62},"string\n",[56,1098,1099],{"class":58,"line":410},[56,1100,130],{"class":74},[56,1102,1103],{"class":58,"line":415},[56,1104,373],{"emptyLinePlaceholder":372},[56,1106,1107],{"class":58,"line":421},[56,1108,1109],{"class":123},"\u002F\u002F só lê o parâmetro\n",[56,1111,1112,1114,1117,1119,1122,1124,1127,1129,1131],{"class":58,"line":468},[56,1113,145],{"class":66},[56,1115,1116],{"class":70}," isPaid",[56,1118,75],{"class":74},[56,1120,1121],{"class":431},"o",[56,1123,67],{"class":66},[56,1125,1126],{"class":156},"Order",[56,1128,81],{"class":74},[56,1130,441],{"class":62},[56,1132,84],{"class":74},[56,1134,1135,1137,1140,1142,1145,1148,1152,1155],{"class":58,"line":473},[56,1136,111],{"class":110},[56,1138,1139],{"class":164}," o",[56,1141,453],{"class":74},[56,1143,1144],{"class":164},"Status ",[56,1146,1147],{"class":66},"==",[56,1149,1151],{"class":1150},"sjJ54"," \"",[56,1153,1154],{"class":602},"paid",[56,1156,1157],{"class":1150},"\"\n",[56,1159,1160],{"class":58,"line":487},[56,1161,130],{"class":74},[56,1163,1164],{"class":58,"line":506},[56,1165,373],{"emptyLinePlaceholder":372},[56,1167,1168],{"class":58,"line":521},[56,1169,1170],{"class":123},"\u002F\u002F devolve o parâmetro\n",[56,1172,1173,1175,1178,1180,1183,1186,1188,1190,1192,1194,1196],{"class":58,"line":536},[56,1174,145],{"class":66},[56,1176,1177],{"class":70}," FirstPaid",[56,1179,75],{"class":74},[56,1181,1182],{"class":431},"orders",[56,1184,1185],{"class":74}," []",[56,1187,737],{"class":66},[56,1189,1126],{"class":156},[56,1191,81],{"class":74},[56,1193,67],{"class":66},[56,1195,1126],{"class":156},[56,1197,84],{"class":74},[56,1199,1200,1203,1206,1208,1211,1213,1216,1219],{"class":58,"line":550},[56,1201,1202],{"class":110},"    for",[56,1204,1205],{"class":164}," _",[56,1207,648],{"class":74},[56,1209,1210],{"class":164}," o ",[56,1212,168],{"class":66},[56,1214,1215],{"class":110}," range",[56,1217,1218],{"class":164}," orders ",[56,1220,1221],{"class":74},"{\n",[56,1223,1224,1227,1229,1231,1233,1235],{"class":58,"line":865},[56,1225,1226],{"class":110},"        if",[56,1228,1116],{"class":70},[56,1230,75],{"class":74},[56,1232,1121],{"class":164},[56,1234,81],{"class":74},[56,1236,84],{"class":74},[56,1238,1239,1242],{"class":58,"line":870},[56,1240,1241],{"class":110},"            return",[56,1243,1244],{"class":164}," o\n",[56,1246,1247],{"class":58,"line":875},[56,1248,1249],{"class":74},"        }\n",[56,1251,1252],{"class":58,"line":881},[56,1253,1254],{"class":74},"    }\n",[56,1256,1257,1259],{"class":58,"line":907},[56,1258,111],{"class":110},[56,1260,1262],{"class":1261},"s39Yj"," nil\n",[56,1264,1265],{"class":58,"line":934},[56,1266,130],{"class":74},[56,1268,1270],{"class":58,"line":1269},23,[56,1271,373],{"emptyLinePlaceholder":372},[56,1273,1275,1277,1280,1282],{"class":58,"line":1274},24,[56,1276,378],{"class":66},[56,1278,1279],{"class":156}," Registry",[56,1281,384],{"class":66},[56,1283,84],{"class":74},[56,1285,1287,1290,1293,1296,1299,1302,1304],{"class":58,"line":1286},25,[56,1288,1289],{"class":164},"    byID ",[56,1291,1292],{"class":66},"map",[56,1294,1295],{"class":74},"[",[56,1297,1298],{"class":62},"int64",[56,1300,1301],{"class":74},"]",[56,1303,737],{"class":66},[56,1305,1306],{"class":156},"Order\n",[56,1308,1310],{"class":58,"line":1309},26,[56,1311,130],{"class":74},[56,1313,1315],{"class":58,"line":1314},27,[56,1316,373],{"emptyLinePlaceholder":372},[56,1318,1320],{"class":58,"line":1319},28,[56,1321,1322],{"class":123},"\u002F\u002F guarda o parâmetro em uma estrutura de vida longa\n",[56,1324,1326,1328,1331,1334,1336,1339,1341,1344,1346,1348,1350,1352,1354],{"class":58,"line":1325},29,[56,1327,145],{"class":66},[56,1329,1330],{"class":74}," (",[56,1332,1333],{"class":431},"r ",[56,1335,737],{"class":66},[56,1337,1338],{"class":156},"Registry",[56,1340,81],{"class":74},[56,1342,1343],{"class":70}," Add",[56,1345,75],{"class":74},[56,1347,1121],{"class":431},[56,1349,67],{"class":66},[56,1351,1126],{"class":156},[56,1353,81],{"class":74},[56,1355,84],{"class":74},[56,1357,1359,1362,1364,1367,1369,1371,1373,1376,1378,1381],{"class":58,"line":1358},30,[56,1360,1361],{"class":164},"    r",[56,1363,453],{"class":74},[56,1365,1366],{"class":164},"byID",[56,1368,1295],{"class":74},[56,1370,1121],{"class":164},[56,1372,453],{"class":74},[56,1374,1375],{"class":164},"ID",[56,1377,1301],{"class":74},[56,1379,1380],{"class":66}," =",[56,1382,1244],{"class":164},[56,1384,1386],{"class":58,"line":1385},31,[56,1387,130],{"class":74},[33,1389,1390],{},"E a saída:",[46,1392,1395],{"className":1393,"code":1394,"language":558,"meta":51},[556],".\u002Forders.go:10:13: o does not escape\n.\u002Forders.go:15:16: leaking param: orders to result ~r0 level=1\n.\u002Forders.go:29:7:  r does not escape\n.\u002Forders.go:29:24: leaking param: o\n",[53,1396,1394],{"__ignoreMap":51},[33,1398,1399],{},"Traduzindo cada linha:",[1401,1402,1403,1412,1429,1445],"ul",{},[1404,1405,1406,1411],"li",{},[195,1407,1408],{},[53,1409,1410],{},"does not escape",": o compilador provou que o valor não sobrevive à chamada. É a resposta que você quer.",[1404,1413,1414,1419,1420,1025,1422,1424,1425,1428],{},[195,1415,1416],{},[53,1417,1418],{},"leaking param: o",": o parâmetro vaza sem qualificação, ou seja, vai parar no heap (aqui, dentro do ",[53,1421,1292],{},[53,1423,1338],{},"). Quem chamar ",[53,1426,1427],{},"Add"," vai ter o argumento alocado no heap.",[1404,1430,1431,1436,1437,1440,1441,1444],{},[195,1432,1433],{},[53,1434,1435],{},"leaking param: orders to result ~r0 level=1",": vazamento ",[299,1438,1439],{},"condicional",". O parâmetro não vai para o heap; ele sai pelo resultado da função. O ",[53,1442,1443],{},"level=1"," é o número de dereferências: o que sai é o conteúdo do slice, não o slice em si. Isso é o que permite ao compilador analisar quem chama sem reanalisar a função inteira.",[1404,1446,1447,1452],{},[195,1448,1449],{},[53,1450,1451],{},"moved to heap: x",": aparece quando uma variável declarada localmente precisou virar alocação no heap.",[33,1454,1455,1456,1459,1460,1463,1464,1467,1468,1471],{},"Essa distinção entre \"vaza para o heap\" e \"vaza para o resultado\" é o coração da parte interprocedural. O compilador resume cada função em ",[195,1457,1458],{},"parameter tags"," e usa esse resumo nos pontos de chamada, em vez de reanalisar tudo. Internamente, ele monta um grafo dirigido em que os vértices são variáveis e as arestas são atribuições, com peso igual ao número de dereferências menos o número de operações de endereço (",[53,1461,1462],{},"p = &q"," pesa -1, ",[53,1465,1466],{},"p = q"," pesa 0, ",[53,1469,1470],{},"p = *q"," pesa 1). Depois procura caminhos que violem as duas regras.",[217,1473,1475],{"id":1474},"parte-3-o-que-realmente-faz-um-valor-escapar","Parte 3: o que realmente faz um valor escapar",[33,1477,1478],{},"Na prática, quase todo escape em código de aplicação cai em um destes cinco padrões.",[1037,1480,1482],{"id":1481},"_1-o-ponteiro-sobrevive-ao-frame","1. O ponteiro sobrevive ao frame",[33,1484,1485,1486,1489,1490,1493],{},"Já vimos: retornar ",[53,1487,1488],{},"&local",", enviar um ponteiro por channel, guardar em uma variável global. É o caso óbvio, e normalmente é o caso ",[299,1491,1492],{},"correto",": se o dado precisa viver mais que a chamada, ele tem que estar no heap.",[33,1495,1496,1497,1500],{},"O que não é óbvio é que ",[195,1498,1499],{},"isso interage com o inlining",". Se o construtor for inlinado no chamador, a fronteira que causava o escape simplesmente deixa de existir:",[46,1502,1504],{"className":136,"code":1503,"language":138,"meta":51,"style":51},"package inl\n\ntype Point struct{ X, Y int }\n\nfunc newPoint(x, y int) *Point { return &Point{X: x, Y: y} }\n\nfunc Norm(x, y int) int {\n    p := newPoint(x, y) \u002F\u002F inlinado aqui\n    return p.X*p.X + p.Y*p.Y\n}\n",[53,1505,1506,1513,1517,1538,1542,1594,1598,1621,1642,1676],{"__ignoreMap":51},[56,1507,1508,1510],{"class":58,"line":59},[56,1509,364],{"class":66},[56,1511,1512],{"class":156}," inl\n",[56,1514,1515],{"class":58,"line":87},[56,1516,373],{"emptyLinePlaceholder":372},[56,1518,1519,1521,1523,1525,1527,1530,1532,1534,1536],{"class":58,"line":107},[56,1520,378],{"class":66},[56,1522,636],{"class":156},[56,1524,384],{"class":66},[56,1526,703],{"class":74},[56,1528,1529],{"class":164}," X",[56,1531,648],{"class":74},[56,1533,651],{"class":164},[56,1535,63],{"class":62},[56,1537,465],{"class":74},[56,1539,1540],{"class":58,"line":127},[56,1541,373],{"emptyLinePlaceholder":372},[56,1543,1544,1546,1549,1551,1553,1555,1557,1559,1561,1563,1565,1567,1569,1571,1573,1575,1577,1579,1581,1583,1585,1587,1589,1592],{"class":58,"line":397},[56,1545,145],{"class":66},[56,1547,1548],{"class":70}," newPoint",[56,1550,75],{"class":74},[56,1552,117],{"class":431},[56,1554,648],{"class":74},[56,1556,682],{"class":431},[56,1558,685],{"class":62},[56,1560,81],{"class":74},[56,1562,67],{"class":66},[56,1564,810],{"class":156},[56,1566,444],{"class":74},[56,1568,447],{"class":110},[56,1570,114],{"class":66},[56,1572,810],{"class":156},[56,1574,703],{"class":74},[56,1576,706],{"class":164},[56,1578,709],{"class":74},[56,1580,712],{"class":164},[56,1582,648],{"class":74},[56,1584,717],{"class":164},[56,1586,709],{"class":74},[56,1588,682],{"class":164},[56,1590,1591],{"class":74},"}",[56,1593,465],{"class":74},[56,1595,1596],{"class":58,"line":405},[56,1597,373],{"emptyLinePlaceholder":372},[56,1599,1600,1602,1605,1607,1609,1611,1613,1615,1617,1619],{"class":58,"line":410},[56,1601,145],{"class":66},[56,1603,1604],{"class":70}," Norm",[56,1606,75],{"class":74},[56,1608,117],{"class":431},[56,1610,648],{"class":74},[56,1612,682],{"class":431},[56,1614,685],{"class":62},[56,1616,81],{"class":74},[56,1618,685],{"class":62},[56,1620,84],{"class":74},[56,1622,1623,1625,1627,1629,1631,1633,1635,1637,1639],{"class":58,"line":415},[56,1624,696],{"class":164},[56,1626,168],{"class":66},[56,1628,1548],{"class":70},[56,1630,75],{"class":74},[56,1632,117],{"class":164},[56,1634,648],{"class":74},[56,1636,682],{"class":164},[56,1638,81],{"class":74},[56,1640,1641],{"class":123}," \u002F\u002F inlinado aqui\n",[56,1643,1644,1646,1648,1650,1652,1654,1656,1658,1660,1662,1664,1666,1668,1670,1672,1674],{"class":58,"line":421},[56,1645,111],{"class":110},[56,1647,730],{"class":164},[56,1649,453],{"class":74},[56,1651,706],{"class":164},[56,1653,737],{"class":66},[56,1655,33],{"class":164},[56,1657,453],{"class":74},[56,1659,744],{"class":164},[56,1661,747],{"class":66},[56,1663,730],{"class":164},[56,1665,453],{"class":74},[56,1667,754],{"class":164},[56,1669,737],{"class":66},[56,1671,33],{"class":164},[56,1673,453],{"class":74},[56,1675,763],{"class":164},[56,1677,1678],{"class":58,"line":468},[56,1679,130],{"class":74},[46,1681,1684],{"className":1682,"code":1683,"language":558,"meta":51},[556],".\u002Finl.go:8:15: inlining call to newPoint\n.\u002Finl.go:5:41: &Point{...} escapes to heap     \u002F\u002F newPoint analisada sozinha\n.\u002Finl.go:8:15: &Point{...} does not escape     \u002F\u002F depois de inlinar em Norm\n",[53,1685,1683],{"__ignoreMap":51},[33,1687,1688,1689,1692,1693,1696,1697,1700],{},"A mesma expressão aparece duas vezes com veredictos opostos. Não é inconsistência: o compilador roda a etapa de ",[299,1690,1691],{},"devirtualização e inlining"," ",[195,1694,1695],{},"antes"," da escape analysis, então o que ele analisa é o código já expandido. Medindo com ",[53,1698,1699],{},"-benchmem",", a diferença é exatamente uma alocação:",[46,1702,1705],{"className":1703,"code":1704,"language":558,"meta":51},[556],"BenchmarkInlined-8     1000000000    1.065 ns\u002Fop     0 B\u002Fop    0 allocs\u002Fop\nBenchmarkNoInline-8      64799066   18.47  ns\u002Fop    16 B\u002Fop    1 allocs\u002Fop\n",[53,1706,1704],{"__ignoreMap":51},[203,1708,1709],{},[33,1710,1711,1712,1715,1716,1719],{},"Nesse benchmark, a segunda versão só é diferente por causa de um ",[53,1713,1714],{},"\u002F\u002Fgo:noinline",". A moral não é \"use noinline\", é o contrário: ",[195,1717,1718],{},"o escape analysis depende do inlining",", e por isso a resposta para \"isso aloca?\" depende do contexto da chamada, não só do corpo da função.",[1037,1721,1723],{"id":1722},"_2-boxing-em-interface","2. Boxing em interface",[33,1725,1726,1727,1729,1730,1733],{},"Esse é o mais comum em código real e o mais fácil de não perceber, porque ele não tem ",[53,1728,279],{}," nenhum. Uma interface em Go é um par (tipo, ponteiro para o valor). Guardar um valor concreto em uma interface, ",[53,1731,1732],{},"any"," incluso, significa que esse valor precisa de um endereço, e é aí que a escape analysis entra: se o compilador não conseguir provar que a interface fica contida na função, o valor empacotado vai para o heap.",[33,1735,1736],{},"O exemplo mais frequente é logging estruturado:",[46,1738,1740],{"className":136,"code":1739,"language":138,"meta":51,"style":51},"\u002F\u002F chaves e valores soltos: cada valor vira um \"any\"\nfunc logSlowQuery(query string, elapsed time.Duration) {\n    logger.Warn(\"slow query\", \"query\", query, \"elapsed\", elapsed)\n}\n\n\u002F\u002F atributos tipados: nada precisa ser empacotado em any\nfunc logSlowQueryAttrs(ctx context.Context, query string, elapsed time.Duration) {\n    logger.LogAttrs(ctx, slog.LevelWarn, \"slow query\",\n        slog.String(\"query\", query),\n        slog.Duration(\"elapsed\", elapsed),\n    )\n}\n",[53,1741,1742,1747,1779,1827,1831,1835,1840,1880,1914,1939,1961,1966],{"__ignoreMap":51},[56,1743,1744],{"class":58,"line":59},[56,1745,1746],{"class":123},"\u002F\u002F chaves e valores soltos: cada valor vira um \"any\"\n",[56,1748,1749,1751,1754,1756,1759,1762,1764,1767,1770,1772,1775,1777],{"class":58,"line":87},[56,1750,145],{"class":66},[56,1752,1753],{"class":70}," logSlowQuery",[56,1755,75],{"class":74},[56,1757,1758],{"class":431},"query",[56,1760,1761],{"class":62}," string",[56,1763,648],{"class":74},[56,1765,1766],{"class":431}," elapsed",[56,1768,1769],{"class":156}," time",[56,1771,453],{"class":74},[56,1773,1774],{"class":156},"Duration",[56,1776,81],{"class":74},[56,1778,84],{"class":74},[56,1780,1781,1784,1786,1789,1791,1794,1797,1799,1801,1803,1805,1807,1809,1812,1814,1816,1819,1821,1823,1825],{"class":58,"line":107},[56,1782,1783],{"class":164},"    logger",[56,1785,453],{"class":74},[56,1787,1788],{"class":70},"Warn",[56,1790,75],{"class":74},[56,1792,1793],{"class":1150},"\"",[56,1795,1796],{"class":602},"slow query",[56,1798,1793],{"class":1150},[56,1800,648],{"class":74},[56,1802,1151],{"class":1150},[56,1804,1758],{"class":602},[56,1806,1793],{"class":1150},[56,1808,648],{"class":74},[56,1810,1811],{"class":164}," query",[56,1813,648],{"class":74},[56,1815,1151],{"class":1150},[56,1817,1818],{"class":602},"elapsed",[56,1820,1793],{"class":1150},[56,1822,648],{"class":74},[56,1824,1766],{"class":164},[56,1826,547],{"class":74},[56,1828,1829],{"class":58,"line":127},[56,1830,130],{"class":74},[56,1832,1833],{"class":58,"line":397},[56,1834,373],{"emptyLinePlaceholder":372},[56,1836,1837],{"class":58,"line":405},[56,1838,1839],{"class":123},"\u002F\u002F atributos tipados: nada precisa ser empacotado em any\n",[56,1841,1842,1844,1847,1849,1852,1855,1857,1860,1862,1864,1866,1868,1870,1872,1874,1876,1878],{"class":58,"line":410},[56,1843,145],{"class":66},[56,1845,1846],{"class":70}," logSlowQueryAttrs",[56,1848,75],{"class":74},[56,1850,1851],{"class":431},"ctx",[56,1853,1854],{"class":156}," context",[56,1856,453],{"class":74},[56,1858,1859],{"class":156},"Context",[56,1861,648],{"class":74},[56,1863,1811],{"class":431},[56,1865,1761],{"class":62},[56,1867,648],{"class":74},[56,1869,1766],{"class":431},[56,1871,1769],{"class":156},[56,1873,453],{"class":74},[56,1875,1774],{"class":156},[56,1877,81],{"class":74},[56,1879,84],{"class":74},[56,1881,1882,1884,1886,1889,1891,1893,1895,1898,1900,1903,1905,1907,1909,1911],{"class":58,"line":415},[56,1883,1783],{"class":164},[56,1885,453],{"class":74},[56,1887,1888],{"class":70},"LogAttrs",[56,1890,75],{"class":74},[56,1892,1851],{"class":164},[56,1894,648],{"class":74},[56,1896,1897],{"class":164}," slog",[56,1899,453],{"class":74},[56,1901,1902],{"class":164},"LevelWarn",[56,1904,648],{"class":74},[56,1906,1151],{"class":1150},[56,1908,1796],{"class":602},[56,1910,1793],{"class":1150},[56,1912,1913],{"class":74},",\n",[56,1915,1916,1919,1921,1924,1926,1928,1930,1932,1934,1936],{"class":58,"line":421},[56,1917,1918],{"class":164},"        slog",[56,1920,453],{"class":74},[56,1922,1923],{"class":70},"String",[56,1925,75],{"class":74},[56,1927,1793],{"class":1150},[56,1929,1758],{"class":602},[56,1931,1793],{"class":1150},[56,1933,648],{"class":74},[56,1935,1811],{"class":164},[56,1937,1938],{"class":74},"),\n",[56,1940,1941,1943,1945,1947,1949,1951,1953,1955,1957,1959],{"class":58,"line":468},[56,1942,1918],{"class":164},[56,1944,453],{"class":74},[56,1946,1774],{"class":70},[56,1948,75],{"class":74},[56,1950,1793],{"class":1150},[56,1952,1818],{"class":602},[56,1954,1793],{"class":1150},[56,1956,648],{"class":74},[56,1958,1766],{"class":164},[56,1960,1938],{"class":74},[56,1962,1963],{"class":58,"line":473},[56,1964,1965],{"class":74},"    )\n",[56,1967,1968],{"class":58,"line":487},[56,1969,130],{"class":74},[33,1971,1972,1973,1975,1976,1979,1980,1983],{},"Com ",[53,1974,587],{},", a primeira versão acusa ",[53,1977,1978],{},"elapsed escapes to heap",". E com um handler escrevendo em ",[53,1981,1982],{},"io.Discard",", para isolar o custo de formatação:",[46,1985,1988],{"className":1986,"code":1987,"language":558,"meta":51},[556],"BenchmarkLogAny-8     1000000    1028   ns\u002Fop    24 B\u002Fop    1 allocs\u002Fop\nBenchmarkLogAttrs-8   1314451     911.5 ns\u002Fop     0 B\u002Fop    0 allocs\u002Fop\n",[53,1989,1987],{"__ignoreMap":51},[33,1991,1992,1993,1996,1997,2000,2001,2003,2004,2006,2007,2010,2011,2014,2015,2018,2019,2022,2023,2026,2027,2029,2030,2032],{},"Os 24 bytes se decompõem assim: ",[53,1994,1995],{},"Logger.Warn"," recebe ",[53,1998,1999],{},"...any",", então tanto ",[53,2002,1758],{}," quanto ",[53,2005,1818],{}," precisam ser empacotados. Empacotar a ",[53,2008,2009],{},"string"," custa 16 bytes (o cabeçalho da string precisa de um endereço) e empacotar o ",[53,2012,2013],{},"time.Duration"," custa mais 8. Já ",[53,2016,2017],{},"slog.String"," e ",[53,2020,2021],{},"slog.Duration"," devolvem um ",[53,2024,2025],{},"slog.Attr",", que guarda esses dados em campos próprios da struct, sem passar por ",[53,2028,1732],{},". Zero alocações. É exatamente para isso que a API ",[53,2031,1888],{}," existe.",[249,2034,2035],{},[33,2036,237,2037,2039,2040,2043,2044,2047,2048,2051],{},[53,2038,2013],{}," daquele benchmark começa em 1 e vai crescendo. Se você fixá-lo em um valor pequeno, a alocação de 8 bytes some: o runtime mantém um array estático de 256 ",[53,2041,2042],{},"uint64"," e devolve um ponteiro para dentro dele quando o valor cabe ali (",[53,2045,2046],{},"staticuint64s",", em ",[53,2049,2050],{},"runtime\u002Fiface.go","). É por isso que microbenchmarks com constantes pequenas às vezes mostram zero alocações e o mesmo código em produção mostra uma.",[33,2053,2054,2055,2058],{},"O mesmo vale para ",[53,2056,2057],{},"fmt.Sprintf",". Um caso concreto: montar uma chave de cache num caminho quente.",[46,2060,2062],{"className":136,"code":2061,"language":138,"meta":51,"style":51},"func cacheKeySprintf(tenantID, userID int64) string {\n    return fmt.Sprintf(\"user:%d:%d\", tenantID, userID)\n}\n\nfunc cacheKeyAppend(tenantID, userID int64) string {\n    buf := make([]byte, 0, 32)\n    buf = append(buf, \"user:\"...)\n    buf = strconv.AppendInt(buf, tenantID, 10)\n    buf = append(buf, ':')\n    buf = strconv.AppendInt(buf, userID, 10)\n    return string(buf)\n}\n",[53,2063,2064,2090,2130,2134,2138,2161,2188,2215,2244,2268,2294,2306],{"__ignoreMap":51},[56,2065,2066,2068,2071,2073,2076,2078,2081,2084,2086,2088],{"class":58,"line":59},[56,2067,145],{"class":66},[56,2069,2070],{"class":70}," cacheKeySprintf",[56,2072,75],{"class":74},[56,2074,2075],{"class":431},"tenantID",[56,2077,648],{"class":74},[56,2079,2080],{"class":431}," userID",[56,2082,2083],{"class":62}," int64",[56,2085,81],{"class":74},[56,2087,1761],{"class":62},[56,2089,84],{"class":74},[56,2091,2092,2094,2097,2099,2102,2104,2106,2109,2113,2115,2117,2119,2121,2124,2126,2128],{"class":58,"line":87},[56,2093,111],{"class":110},[56,2095,2096],{"class":164}," fmt",[56,2098,453],{"class":74},[56,2100,2101],{"class":70},"Sprintf",[56,2103,75],{"class":74},[56,2105,1793],{"class":1150},[56,2107,2108],{"class":602},"user:",[56,2110,2112],{"class":2111},"swQdS","%d",[56,2114,709],{"class":602},[56,2116,2112],{"class":2111},[56,2118,1793],{"class":1150},[56,2120,648],{"class":74},[56,2122,2123],{"class":164}," tenantID",[56,2125,648],{"class":74},[56,2127,2080],{"class":164},[56,2129,547],{"class":74},[56,2131,2132],{"class":58,"line":107},[56,2133,130],{"class":74},[56,2135,2136],{"class":58,"line":127},[56,2137,373],{"emptyLinePlaceholder":372},[56,2139,2140,2142,2145,2147,2149,2151,2153,2155,2157,2159],{"class":58,"line":397},[56,2141,145],{"class":66},[56,2143,2144],{"class":70}," cacheKeyAppend",[56,2146,75],{"class":74},[56,2148,2075],{"class":431},[56,2150,648],{"class":74},[56,2152,2080],{"class":431},[56,2154,2083],{"class":62},[56,2156,81],{"class":74},[56,2158,1761],{"class":62},[56,2160,84],{"class":74},[56,2162,2163,2166,2168,2171,2174,2177,2179,2181,2183,2186],{"class":58,"line":405},[56,2164,2165],{"class":164},"    buf ",[56,2167,168],{"class":66},[56,2169,2170],{"class":70}," make",[56,2172,2173],{"class":74},"([]",[56,2175,2176],{"class":62},"byte",[56,2178,648],{"class":74},[56,2180,462],{"class":100},[56,2182,648],{"class":74},[56,2184,2185],{"class":100}," 32",[56,2187,547],{"class":74},[56,2189,2190,2192,2194,2197,2199,2202,2204,2206,2208,2210,2213],{"class":58,"line":410},[56,2191,2165],{"class":164},[56,2193,97],{"class":66},[56,2195,2196],{"class":70}," append",[56,2198,75],{"class":74},[56,2200,2201],{"class":164},"buf",[56,2203,648],{"class":74},[56,2205,1151],{"class":1150},[56,2207,2108],{"class":602},[56,2209,1793],{"class":1150},[56,2211,2212],{"class":66},"...",[56,2214,547],{"class":74},[56,2216,2217,2219,2221,2224,2226,2229,2231,2233,2235,2237,2239,2242],{"class":58,"line":415},[56,2218,2165],{"class":164},[56,2220,97],{"class":66},[56,2222,2223],{"class":164}," strconv",[56,2225,453],{"class":74},[56,2227,2228],{"class":70},"AppendInt",[56,2230,75],{"class":74},[56,2232,2201],{"class":164},[56,2234,648],{"class":74},[56,2236,2123],{"class":164},[56,2238,648],{"class":74},[56,2240,2241],{"class":100}," 10",[56,2243,547],{"class":74},[56,2245,2246,2248,2250,2252,2254,2256,2258,2261,2263,2266],{"class":58,"line":421},[56,2247,2165],{"class":164},[56,2249,97],{"class":66},[56,2251,2196],{"class":70},[56,2253,75],{"class":74},[56,2255,2201],{"class":164},[56,2257,648],{"class":74},[56,2259,2260],{"class":1150}," '",[56,2262,709],{"class":606},[56,2264,2265],{"class":1150},"'",[56,2267,547],{"class":74},[56,2269,2270,2272,2274,2276,2278,2280,2282,2284,2286,2288,2290,2292],{"class":58,"line":468},[56,2271,2165],{"class":164},[56,2273,97],{"class":66},[56,2275,2223],{"class":164},[56,2277,453],{"class":74},[56,2279,2228],{"class":70},[56,2281,75],{"class":74},[56,2283,2201],{"class":164},[56,2285,648],{"class":74},[56,2287,2080],{"class":164},[56,2289,648],{"class":74},[56,2291,2241],{"class":100},[56,2293,547],{"class":74},[56,2295,2296,2298,2300,2302,2304],{"class":58,"line":473},[56,2297,111],{"class":110},[56,2299,1761],{"class":62},[56,2301,75],{"class":74},[56,2303,2201],{"class":164},[56,2305,547],{"class":74},[56,2307,2308],{"class":58,"line":487},[56,2309,130],{"class":74},[46,2311,2314],{"className":2312,"code":2313,"language":558,"meta":51},[556],"BenchmarkSprintf-8   10583786   124.7 ns\u002Fop    24 B\u002Fop    2 allocs\u002Fop\nBenchmarkAppend-8    35509032    47.95 ns\u002Fop   16 B\u002Fop    1 allocs\u002Fop\n",[53,2315,2313],{"__ignoreMap":51},[33,2317,237,2318,2320,2321,2018,2324,2327,2328,2330,2331,2334,2335,2337,2338,2341,2342,2344],{},[53,2319,587],{}," acusa ",[53,2322,2323],{},"tenantID escapes to heap",[53,2325,2326],{},"userID escapes to heap"," na primeira versão, por causa do ",[53,2329,1999],{},". Um profile de memória mostra de onde vêm os 24 bytes: 16 no ",[53,2332,2333],{},"string(p.buf)"," interno do ",[53,2336,2057],{}," e 8 no empacotamento do ",[53,2339,2340],{},"userID",". O ",[53,2343,2075],{}," é a constante 42 e não custa nada, pelo mesmo motivo da observação acima.",[33,2346,2347,2348,2350,2351,2018,2354,2357,2358,2361],{},"Na segunda versão, o ",[53,2349,587],{}," diz ",[53,2352,2353],{},"make([]byte, 0, 32) does not escape",[53,2355,2356],{},"append does not escape",". O buffer inteiro vive na stack, e só o ",[53,2359,2360],{},"string(buf)"," final aloca, porque strings em Go são imutáveis e o resultado precisa sobreviver à função.",[249,2363,2364],{},[33,2365,2366],{},"Repare que a alocação da string devolvida sobrevive nas duas versões, e não tem como ser diferente: alguém precisa ser dono daqueles bytes depois que a função retorna. Escape analysis não elimina alocações necessárias, ela elimina as acidentais.",[1037,2368,2370],{"id":2369},"_3-guardar-um-ponteiro-em-algo-que-já-está-no-heap","3. Guardar um ponteiro em algo que já está no heap",[33,2372,2373],{},"Esta é a regra (1) em ação, e ela é transitiva. Se você escreve um ponteiro dentro de um slice, map, struct ou closure que está no heap, o alvo tem que ir junto. Não existe ponteiro do heap para a stack.",[33,2375,2376,2377,2380,2381,2383,2384,2386],{},"É por isso que o ",[53,2378,2379],{},"Registry.Add"," do exemplo anterior marca ",[53,2382,1418],{}," sem qualificação: o ",[53,2385,1292],{}," sobrevive à chamada, então tudo que entra nele sobrevive também.",[33,2388,2389,2390,2393],{},"Uma consequência que vale internalizar: ",[195,2391,2392],{},"a decisão se propaga para trás",". Uma única linha que guarda um ponteiro em uma estrutura de vida longa faz o valor original escapar, mesmo que ele tenha sido criado dez frames acima.",[1037,2395,2397],{"id":2396},"_4-closures-e-goroutines","4. Closures e goroutines",[33,2399,2400],{},"Uma closure captura variáveis. Se a closure escapa, as variáveis capturadas por referência escapam junto.",[46,2402,2404],{"className":136,"code":2403,"language":138,"meta":51,"style":51},"package jobs\n\nimport \"sync\"\n\ntype Job struct {\n    ID      int\n    Payload string\n}\n\nfunc ProcessAsync(jobs []Job) int {\n    var mu sync.Mutex\n    var wg sync.WaitGroup\n    total := 0\n    for _, j := range jobs {\n        wg.Add(1)\n        go func() {\n            defer wg.Done()\n            mu.Lock()\n            total += len(j.Payload)\n            mu.Unlock()\n        }()\n    }\n    wg.Wait()\n    return total\n}\n",[53,2405,2406,2413,2417,2429,2433,2444,2451,2458,2462,2466,2489,2504,2518,2528,2548,2564,2576,2592,2604,2627,2638,2643,2647,2659,2666],{"__ignoreMap":51},[56,2407,2408,2410],{"class":58,"line":59},[56,2409,364],{"class":66},[56,2411,2412],{"class":156}," jobs\n",[56,2414,2415],{"class":58,"line":87},[56,2416,373],{"emptyLinePlaceholder":372},[56,2418,2419,2422,2424,2427],{"class":58,"line":107},[56,2420,2421],{"class":110},"import",[56,2423,1151],{"class":1150},[56,2425,2426],{"class":156},"sync",[56,2428,1157],{"class":1150},[56,2430,2431],{"class":58,"line":127},[56,2432,373],{"emptyLinePlaceholder":372},[56,2434,2435,2437,2440,2442],{"class":58,"line":397},[56,2436,378],{"class":66},[56,2438,2439],{"class":156}," Job",[56,2441,384],{"class":66},[56,2443,84],{"class":74},[56,2445,2446,2449],{"class":58,"line":405},[56,2447,2448],{"class":164},"    ID      ",[56,2450,394],{"class":62},[56,2452,2453,2456],{"class":58,"line":410},[56,2454,2455],{"class":164},"    Payload ",[56,2457,1096],{"class":62},[56,2459,2460],{"class":58,"line":415},[56,2461,130],{"class":74},[56,2463,2464],{"class":58,"line":421},[56,2465,373],{"emptyLinePlaceholder":372},[56,2467,2468,2470,2473,2475,2478,2480,2483,2485,2487],{"class":58,"line":468},[56,2469,145],{"class":66},[56,2471,2472],{"class":70}," ProcessAsync",[56,2474,75],{"class":74},[56,2476,2477],{"class":431},"jobs",[56,2479,1185],{"class":74},[56,2481,2482],{"class":156},"Job",[56,2484,81],{"class":74},[56,2486,685],{"class":62},[56,2488,84],{"class":74},[56,2490,2491,2494,2497,2499,2501],{"class":58,"line":473},[56,2492,2493],{"class":66},"    var",[56,2495,2496],{"class":164}," mu ",[56,2498,2426],{"class":156},[56,2500,453],{"class":74},[56,2502,2503],{"class":156},"Mutex\n",[56,2505,2506,2508,2511,2513,2515],{"class":58,"line":487},[56,2507,2493],{"class":66},[56,2509,2510],{"class":164}," wg ",[56,2512,2426],{"class":156},[56,2514,453],{"class":74},[56,2516,2517],{"class":156},"WaitGroup\n",[56,2519,2520,2523,2525],{"class":58,"line":506},[56,2521,2522],{"class":164},"    total ",[56,2524,168],{"class":66},[56,2526,2527],{"class":100}," 0\n",[56,2529,2530,2532,2534,2536,2539,2541,2543,2546],{"class":58,"line":521},[56,2531,1202],{"class":110},[56,2533,1205],{"class":164},[56,2535,648],{"class":74},[56,2537,2538],{"class":164}," j ",[56,2540,168],{"class":66},[56,2542,1215],{"class":110},[56,2544,2545],{"class":164}," jobs ",[56,2547,1221],{"class":74},[56,2549,2550,2553,2555,2557,2559,2562],{"class":58,"line":536},[56,2551,2552],{"class":164},"        wg",[56,2554,453],{"class":74},[56,2556,1427],{"class":70},[56,2558,75],{"class":74},[56,2560,2561],{"class":100},"1",[56,2563,547],{"class":74},[56,2565,2566,2569,2572,2574],{"class":58,"line":550},[56,2567,2568],{"class":110},"        go",[56,2570,2571],{"class":66}," func",[56,2573,151],{"class":74},[56,2575,84],{"class":74},[56,2577,2578,2581,2584,2586,2589],{"class":58,"line":865},[56,2579,2580],{"class":110},"            defer",[56,2582,2583],{"class":164}," wg",[56,2585,453],{"class":74},[56,2587,2588],{"class":70},"Done",[56,2590,2591],{"class":74},"()\n",[56,2593,2594,2597,2599,2602],{"class":58,"line":870},[56,2595,2596],{"class":164},"            mu",[56,2598,453],{"class":74},[56,2600,2601],{"class":70},"Lock",[56,2603,2591],{"class":74},[56,2605,2606,2609,2612,2615,2617,2620,2622,2625],{"class":58,"line":875},[56,2607,2608],{"class":164},"            total ",[56,2610,2611],{"class":66},"+=",[56,2613,2614],{"class":70}," len",[56,2616,75],{"class":74},[56,2618,2619],{"class":164},"j",[56,2621,453],{"class":74},[56,2623,2624],{"class":164},"Payload",[56,2626,547],{"class":74},[56,2628,2629,2631,2633,2636],{"class":58,"line":881},[56,2630,2596],{"class":164},[56,2632,453],{"class":74},[56,2634,2635],{"class":70},"Unlock",[56,2637,2591],{"class":74},[56,2639,2640],{"class":58,"line":907},[56,2641,2642],{"class":74},"        }()\n",[56,2644,2645],{"class":58,"line":934},[56,2646,1254],{"class":74},[56,2648,2649,2652,2654,2657],{"class":58,"line":1269},[56,2650,2651],{"class":164},"    wg",[56,2653,453],{"class":74},[56,2655,2656],{"class":70},"Wait",[56,2658,2591],{"class":74},[56,2660,2661,2663],{"class":58,"line":1274},[56,2662,111],{"class":110},[56,2664,2665],{"class":164}," total\n",[56,2667,2668],{"class":58,"line":1286},[56,2669,130],{"class":74},[46,2671,2674],{"className":2672,"code":2673,"language":558,"meta":51},[556],".\u002Fjobs.go:10:19: leaking param content: jobs\n.\u002Fjobs.go:11:6:  moved to heap: mu\n.\u002Fjobs.go:12:6:  moved to heap: wg\n.\u002Fjobs.go:13:2:  moved to heap: total\n.\u002Fjobs.go:16:6:  func literal escapes to heap\n",[53,2675,2673],{"__ignoreMap":51},[33,2677,2678,2679,2682,2683,2018,2686,2689],{},"Toda goroutine tem sua própria stack, então uma variável compartilhada entre a goroutine pai e a filha não pode viver na stack de nenhuma das duas. ",[53,2680,2681],{},"mu",", ",[53,2684,2685],{},"wg",[53,2687,2688],{},"total"," vão para o heap porque a closure as modifica e precisa do endereço.",[33,2691,2692,2693,2696,2697,2699,2700,2702],{},"Repare no que ",[195,2694,2695],{},"não"," está na lista: ",[53,2698,2619],{},". Como ",[53,2701,2619],{}," só é lido, o compilador a captura por cópia, guardando o valor direto dentro do objeto da closure em vez de criar uma variável separada no heap. É um detalhe pequeno, mas mostra que \"capturou\" e \"escapou\" não são a mesma coisa.",[203,2704,2705],{},[33,2706,2707,2708,2711,2712,2715,2716,2719,2720,453],{},"A mesma função com a closure chamada de forma síncrona (",[53,2709,2710],{},"count(j)"," em vez de ",[53,2713,2714],{},"go func(){}()",") não move nada para o heap: o compilador imprime apenas ",[53,2717,2718],{},"jobs does not escape",". O que causa o escape não é a closure, é o ",[53,2721,138],{},[1037,2723,2725],{"id":2724},"_5-tamanho-grande-demais-para-a-stack","5. Tamanho: grande demais para a stack",[33,2727,2728,2729,2732,2733,453],{},"Independentemente do tempo de vida, existe um teto. Se o valor for maior que o limite, ele vai para o heap, e o ",[53,2730,2731],{},"-m=2"," diz o motivo com todas as letras: ",[53,2734,2735],{},"too large for stack",[33,2737,2738],{},"No Go 1.26 os limites são:",[960,2740,2741,2751],{},[963,2742,2743],{},[966,2744,2745,2748],{},[969,2746,2747],{},"O que",[969,2749,2750],{},"Limite",[976,2752,2753,2767],{},[966,2754,2755,2764],{},[981,2756,2757,2758,2682,2761,81],{},"Declarações explícitas (",[53,2759,2760],{},"var x T",[53,2762,2763],{},"x := ...",[981,2765,2766],{},"128 KiB",[966,2768,2769,2780],{},[981,2770,2771,2772,2682,2774,2682,2776,2779],{},"Alocações implícitas (",[53,2773,353],{},[53,2775,275],{},[53,2777,2778],{},"make([]T, n)"," constante)",[981,2781,2782],{},"64 KiB",[46,2784,2786],{"className":136,"code":2785,"language":138,"meta":51,"style":51},"\u002F\u002Fgo:noinline\nfunc checksum(b []byte) int {\n    t := 0\n    for _, v := range b {\n        t += int(v)\n    }\n    return t\n}\n\nfunc small() int {\n    var buf [64 * 1024]byte \u002F\u002F 64 KiB: fica na stack\n    return checksum(buf[:])\n}\n\nfunc big() int {\n    var buf [129 * 1024]byte \u002F\u002F 129 KiB: moved to heap\n    return checksum(buf[:])\n}\n",[53,2787,2788,2792,2814,2823,2843,2859,2863,2870,2874,2878,2891,2915,2928,2932,2936,2949,2971,2983],{"__ignoreMap":51},[56,2789,2790],{"class":58,"line":59},[56,2791,418],{"class":123},[56,2793,2794,2796,2799,2801,2804,2806,2808,2810,2812],{"class":58,"line":87},[56,2795,145],{"class":66},[56,2797,2798],{"class":70}," checksum",[56,2800,75],{"class":74},[56,2802,2803],{"class":431},"b",[56,2805,1185],{"class":74},[56,2807,2176],{"class":62},[56,2809,81],{"class":74},[56,2811,685],{"class":62},[56,2813,84],{"class":74},[56,2815,2816,2819,2821],{"class":58,"line":107},[56,2817,2818],{"class":164},"    t ",[56,2820,168],{"class":66},[56,2822,2527],{"class":100},[56,2824,2825,2827,2829,2831,2834,2836,2838,2841],{"class":58,"line":127},[56,2826,1202],{"class":110},[56,2828,1205],{"class":164},[56,2830,648],{"class":74},[56,2832,2833],{"class":164}," v ",[56,2835,168],{"class":66},[56,2837,1215],{"class":110},[56,2839,2840],{"class":164}," b ",[56,2842,1221],{"class":74},[56,2844,2845,2848,2850,2852,2854,2857],{"class":58,"line":397},[56,2846,2847],{"class":164},"        t ",[56,2849,2611],{"class":66},[56,2851,685],{"class":62},[56,2853,75],{"class":74},[56,2855,2856],{"class":164},"v",[56,2858,547],{"class":74},[56,2860,2861],{"class":58,"line":405},[56,2862,1254],{"class":74},[56,2864,2865,2867],{"class":58,"line":410},[56,2866,111],{"class":110},[56,2868,2869],{"class":164}," t\n",[56,2871,2872],{"class":58,"line":415},[56,2873,130],{"class":74},[56,2875,2876],{"class":58,"line":421},[56,2877,373],{"emptyLinePlaceholder":372},[56,2879,2880,2882,2885,2887,2889],{"class":58,"line":468},[56,2881,145],{"class":66},[56,2883,2884],{"class":70}," small",[56,2886,151],{"class":74},[56,2888,685],{"class":62},[56,2890,84],{"class":74},[56,2892,2893,2895,2898,2900,2903,2905,2908,2910,2912],{"class":58,"line":473},[56,2894,2493],{"class":66},[56,2896,2897],{"class":164}," buf ",[56,2899,1295],{"class":74},[56,2901,2902],{"class":100},"64",[56,2904,67],{"class":66},[56,2906,2907],{"class":100}," 1024",[56,2909,1301],{"class":164},[56,2911,2176],{"class":62},[56,2913,2914],{"class":123}," \u002F\u002F 64 KiB: fica na stack\n",[56,2916,2917,2919,2921,2923,2925],{"class":58,"line":487},[56,2918,111],{"class":110},[56,2920,2798],{"class":70},[56,2922,75],{"class":74},[56,2924,2201],{"class":164},[56,2926,2927],{"class":74},"[:])\n",[56,2929,2930],{"class":58,"line":506},[56,2931,130],{"class":74},[56,2933,2934],{"class":58,"line":521},[56,2935,373],{"emptyLinePlaceholder":372},[56,2937,2938,2940,2943,2945,2947],{"class":58,"line":536},[56,2939,145],{"class":66},[56,2941,2942],{"class":70}," big",[56,2944,151],{"class":74},[56,2946,685],{"class":62},[56,2948,84],{"class":74},[56,2950,2951,2953,2955,2957,2960,2962,2964,2966,2968],{"class":58,"line":550},[56,2952,2493],{"class":66},[56,2954,2897],{"class":164},[56,2956,1295],{"class":74},[56,2958,2959],{"class":100},"129",[56,2961,67],{"class":66},[56,2963,2907],{"class":100},[56,2965,1301],{"class":164},[56,2967,2176],{"class":62},[56,2969,2970],{"class":123}," \u002F\u002F 129 KiB: moved to heap\n",[56,2972,2973,2975,2977,2979,2981],{"class":58,"line":865},[56,2974,111],{"class":110},[56,2976,2798],{"class":70},[56,2978,75],{"class":74},[56,2980,2201],{"class":164},[56,2982,2927],{"class":74},[56,2984,2985],{"class":58,"line":870},[56,2986,130],{"class":74},[33,2988,2989,2990,2992,2993,1692,2996,2999],{},"Existe ainda um caso à parte: ",[53,2991,2778],{}," com ",[53,2994,2995],{},"n",[195,2997,2998],{},"não constante",". Aqui o compilador não sabe o tamanho em tempo de compilação, então, desde o Go 1.25, ele gera as duas versões e escolhe em tempo de execução, reservando uma pequena área fixa na stack (32 bytes por padrão) e caindo para o heap quando não couber:",[46,3001,3004],{"className":3002,"code":3003,"language":558,"meta":51},[556],"BenchmarkVarMakeSmall-8   160536319    7.452 ns\u002Fop     0 B\u002Fop    0 allocs\u002Fop   \u002F\u002F n = 16\nBenchmarkVarMakeLarge-8    19328575   63.39  ns\u002Fop    64 B\u002Fop    1 allocs\u002Fop   \u002F\u002F n = 64\n",[53,3005,3003],{"__ignoreMap":51},[33,3007,3008,3009,2682,3012,3015,3016,3018],{},"Os dois benchmarks rodam a ",[195,3010,3011],{},"mesma linha de código",[53,3013,3014],{},"make([]byte, n)",", com valores diferentes de ",[53,3017,2995],{},". É provavelmente o exemplo mais literal de que \"essa linha aloca?\" não é uma pergunta bem formada em Go.",[217,3020,3022],{"id":3021},"parte-4-medindo-não-adivinhando","Parte 4: medindo, não adivinhando",[33,3024,3025,3026,3028],{},"Chegando aqui, a tentação é sair rodando ",[53,3027,581],{}," no projeto inteiro. Não faça isso: um serviço médio produz milhares de linhas de saída, a maioria irrelevante. Escape analysis é uma ferramenta de diagnóstico, não de auditoria.",[33,3030,3031],{},"O fluxo que funciona é o inverso, e começa pela alocação, não pelo escape:",[33,3033,3034,1692,3037,3039,3040,3043],{},[195,3035,3036],{},"1. Meça alocações onde importa.",[53,3038,1699],{}," mostra ",[53,3041,3042],{},"allocs\u002Fop",", que é o número que se traduz direto em pressão de GC:",[46,3045,3047],{"className":591,"code":3046,"language":593,"meta":51,"style":51},"go test -run='^$' -bench=. -benchmem .\u002F...\n",[53,3048,3049],{"__ignoreMap":51},[56,3050,3051,3053,3056,3059,3061,3064,3066,3069,3072],{"class":58,"line":59},[56,3052,138],{"class":156},[56,3054,3055],{"class":602}," test",[56,3057,3058],{"class":606}," -run=",[56,3060,2265],{"class":1150},[56,3062,3063],{"class":602},"^$",[56,3065,2265],{"class":1150},[56,3067,3068],{"class":606}," -bench=.",[56,3070,3071],{"class":606}," -benchmem",[56,3073,610],{"class":602},[33,3075,3076,3079],{},[195,3077,3078],{},"2. Descubra quem aloca."," Um profile de memória aponta as linhas responsáveis:",[46,3081,3083],{"className":591,"code":3082,"language":593,"meta":51,"style":51},"go test -run='^$' -bench=BenchmarkSprintf -memprofile=mem.out .\u002Fkey\u002F\ngo tool pprof -top -sample_index=alloc_objects key.test mem.out\n",[53,3084,3085,3108],{"__ignoreMap":51},[56,3086,3087,3089,3091,3093,3095,3097,3099,3102,3105],{"class":58,"line":59},[56,3088,138],{"class":156},[56,3090,3055],{"class":602},[56,3092,3058],{"class":606},[56,3094,2265],{"class":1150},[56,3096,3063],{"class":602},[56,3098,2265],{"class":1150},[56,3100,3101],{"class":606}," -bench=BenchmarkSprintf",[56,3103,3104],{"class":606}," -memprofile=mem.out",[56,3106,3107],{"class":602}," .\u002Fkey\u002F\n",[56,3109,3110,3112,3115,3118,3121,3124,3127],{"class":58,"line":87},[56,3111,138],{"class":156},[56,3113,3114],{"class":602}," tool",[56,3116,3117],{"class":602}," pprof",[56,3119,3120],{"class":606}," -top",[56,3122,3123],{"class":606}," -sample_index=alloc_objects",[56,3125,3126],{"class":602}," key.test",[56,3128,3129],{"class":602}," mem.out\n",[46,3131,3134],{"className":3132,"code":3133,"language":558,"meta":51},[556],"      flat  flat%   sum%        cum   cum%\n  10780836 67.00% 67.00%   10780836 67.00%  fmt.Sprintf\n   5308497 32.99%   100%   16089333   100%  key.cacheKeySprintf (inline)\n",[53,3135,3133],{"__ignoreMap":51},[33,3137,3138,3139,3142,3143,3146,3147,3150],{},"Use ",[53,3140,3141],{},"alloc_objects"," (quantidade) e ",[53,3144,3145],{},"alloc_space"," (bytes) para investigar pressão de GC. O default do pprof é ",[53,3148,3149],{},"inuse_space",", que responde outra pergunta: o que ainda está vivo agora.",[33,3152,3153,3159,3160,3163],{},[195,3154,3155,3156,3158],{},"3. Só então use ",[53,3157,581],{},", no pacote que o profile apontou",", para entender ",[299,3161,3162],{},"por que"," aquela linha específica aloca.",[33,3165,3166],{},"Nessa ordem, você gasta esforço só onde tem retorno. Na ordem inversa, você reescreve código legível para eliminar alocações que aconteciam três vezes por requisição.",[217,3168,3170],{"id":3169},"limitações-exceções-e-armadilhas","Limitações, exceções e armadilhas",[33,3172,3173],{},"Esta é a parte que separa entender o mecanismo de usá-lo bem.",[33,3175,3176,1692,3179,3181,3182,3185],{},[195,3177,3178],{},"A análise é conservadora, e isso é assimétrico.",[53,3180,1410],{}," é uma prova: pode confiar. ",[53,3183,3184],{},"escapes to heap"," é a ausência de prova, e pode perfeitamente ser um falso positivo. O caso mais comum é a chamada indireta:",[46,3187,3189],{"className":136,"code":3188,"language":138,"meta":51,"style":51},"package metrics\n\ntype Metric struct {\n    Name  string\n    Value float64\n}\n\nfunc report(m *Metric) { _ = m.Value }\n\nfunc DirectCall() {\n    m := &Metric{Name: \"latency\", Value: 1.5}\n    report(m) \u002F\u002F chamada estática\n}\n\nfunc IndirectCall(fn func(*Metric)) {\n    m := &Metric{Name: \"latency\", Value: 1.5}\n    fn(m) \u002F\u002F chamada indireta\n}\n",[53,3190,3191,3198,3202,3213,3220,3228,3232,3236,3272,3276,3287,3324,3338,3342,3346,3371,3403,3417],{"__ignoreMap":51},[56,3192,3193,3195],{"class":58,"line":59},[56,3194,364],{"class":66},[56,3196,3197],{"class":156}," metrics\n",[56,3199,3200],{"class":58,"line":87},[56,3201,373],{"emptyLinePlaceholder":372},[56,3203,3204,3206,3209,3211],{"class":58,"line":107},[56,3205,378],{"class":66},[56,3207,3208],{"class":156}," Metric",[56,3210,384],{"class":66},[56,3212,84],{"class":74},[56,3214,3215,3218],{"class":58,"line":127},[56,3216,3217],{"class":164},"    Name  ",[56,3219,1096],{"class":62},[56,3221,3222,3225],{"class":58,"line":397},[56,3223,3224],{"class":164},"    Value ",[56,3226,3227],{"class":62},"float64\n",[56,3229,3230],{"class":58,"line":405},[56,3231,130],{"class":74},[56,3233,3234],{"class":58,"line":410},[56,3235,373],{"emptyLinePlaceholder":372},[56,3237,3238,3240,3243,3245,3248,3250,3253,3255,3257,3260,3262,3265,3267,3270],{"class":58,"line":415},[56,3239,145],{"class":66},[56,3241,3242],{"class":70}," report",[56,3244,75],{"class":74},[56,3246,3247],{"class":431},"m",[56,3249,67],{"class":66},[56,3251,3252],{"class":156},"Metric",[56,3254,81],{"class":74},[56,3256,444],{"class":74},[56,3258,3259],{"class":164}," _ ",[56,3261,97],{"class":66},[56,3263,3264],{"class":164}," m",[56,3266,453],{"class":74},[56,3268,3269],{"class":164},"Value ",[56,3271,130],{"class":74},[56,3273,3274],{"class":58,"line":421},[56,3275,373],{"emptyLinePlaceholder":372},[56,3277,3278,3280,3283,3285],{"class":58,"line":468},[56,3279,145],{"class":66},[56,3281,3282],{"class":70}," DirectCall",[56,3284,151],{"class":74},[56,3286,84],{"class":74},[56,3288,3289,3292,3294,3296,3298,3300,3303,3305,3307,3310,3312,3314,3317,3319,3322],{"class":58,"line":473},[56,3290,3291],{"class":164},"    m ",[56,3293,168],{"class":66},[56,3295,114],{"class":66},[56,3297,3252],{"class":156},[56,3299,703],{"class":74},[56,3301,3302],{"class":164},"Name",[56,3304,709],{"class":74},[56,3306,1151],{"class":1150},[56,3308,3309],{"class":602},"latency",[56,3311,1793],{"class":1150},[56,3313,648],{"class":74},[56,3315,3316],{"class":164}," Value",[56,3318,709],{"class":74},[56,3320,3321],{"class":100}," 1.5",[56,3323,130],{"class":74},[56,3325,3326,3329,3331,3333,3335],{"class":58,"line":487},[56,3327,3328],{"class":70},"    report",[56,3330,75],{"class":74},[56,3332,3247],{"class":164},[56,3334,81],{"class":74},[56,3336,3337],{"class":123}," \u002F\u002F chamada estática\n",[56,3339,3340],{"class":58,"line":506},[56,3341,130],{"class":74},[56,3343,3344],{"class":58,"line":521},[56,3345,373],{"emptyLinePlaceholder":372},[56,3347,3348,3350,3353,3355,3358,3360,3362,3364,3366,3369],{"class":58,"line":536},[56,3349,145],{"class":66},[56,3351,3352],{"class":70}," IndirectCall",[56,3354,75],{"class":74},[56,3356,3357],{"class":431},"fn",[56,3359,2571],{"class":66},[56,3361,75],{"class":74},[56,3363,737],{"class":66},[56,3365,3252],{"class":156},[56,3367,3368],{"class":74},"))",[56,3370,84],{"class":74},[56,3372,3373,3375,3377,3379,3381,3383,3385,3387,3389,3391,3393,3395,3397,3399,3401],{"class":58,"line":550},[56,3374,3291],{"class":164},[56,3376,168],{"class":66},[56,3378,114],{"class":66},[56,3380,3252],{"class":156},[56,3382,703],{"class":74},[56,3384,3302],{"class":164},[56,3386,709],{"class":74},[56,3388,1151],{"class":1150},[56,3390,3309],{"class":602},[56,3392,1793],{"class":1150},[56,3394,648],{"class":74},[56,3396,3316],{"class":164},[56,3398,709],{"class":74},[56,3400,3321],{"class":100},[56,3402,130],{"class":74},[56,3404,3405,3408,3410,3412,3414],{"class":58,"line":865},[56,3406,3407],{"class":70},"    fn",[56,3409,75],{"class":74},[56,3411,3247],{"class":164},[56,3413,81],{"class":74},[56,3415,3416],{"class":123}," \u002F\u002F chamada indireta\n",[56,3418,3419],{"class":58,"line":870},[56,3420,130],{"class":74},[46,3422,3425],{"className":3423,"code":3424,"language":558,"meta":51},[556],".\u002Fmetrics.go:11:7: &Metric{...} does not escape\n.\u002Fmetrics.go:16:7: &Metric{...} escapes to heap\n",[53,3426,3424],{"__ignoreMap":51},[33,3428,3429,3430,3433,3434,3436,3437,453],{},"Corpos idênticos, resultados opostos. Em ",[53,3431,3432],{},"IndirectCall",", o compilador não sabe qual função ",[53,3435,3357],{}," é, então não tem parameter tag para consultar e assume o pior. O mesmo acontece com chamadas de método por interface que a devirtualização não consegue resolver, e com ",[53,3438,3439],{},"reflect",[33,3441,3442,3447],{},[195,3443,1043,3444,3446],{},[53,3445,587],{}," não é uma API estável."," Ela é uma ferramenta de diagnóstico do compilador; o formato e o conteúdo mudam entre versões. Não construa testes ou CI em cima dela.",[33,3449,3450,3453,3454,3457],{},[195,3451,3452],{},"Stack também não é de graça."," Frames grandes empurram a goroutine para perto do limite da stack, e crescer a stack significa alocar uma área maior e ",[195,3455,3456],{},"copiar"," a stack inteira, corrigindo os ponteiros internos. Trocar uma alocação de heap por um array de 100 KiB na stack de uma função recursiva pode piorar as coisas.",[33,3459,3460,3466,3467,3470],{},[195,3461,3462,3465],{},[53,3463,3464],{},"0 allocs\u002Fop"," não é sinônimo de rápido."," É um proxy para pressão de GC, que é uma das componentes do custo. Se ",[53,3468,3469],{},"ns\u002Fop"," não melhorou, a mudança não valeu.",[33,3472,3473,3476,3477,3479],{},[195,3474,3475],{},"Não escreva código estranho para agradar o analisador."," Isso é o mais importante da seção. Padrões como quebrar funções para forçar inlining, evitar interfaces em código frio ou substituir ",[53,3478,2057],{}," por concatenação manual em um endpoint chamado dez vezes por dia trocam legibilidade por nada. O critério continua sendo o profile.",[33,3481,3482,3488,3489,3492],{},[195,3483,3484,3487],{},[53,3485,3486],{},"sync.Pool"," não é um substituto."," Ele reduz a taxa de alocação reaproveitando objetos, mas não os tira do heap: a documentação diz explicitamente que \"qualquer item guardado no Pool pode ser removido automaticamente a qualquer momento\", e na prática o GC vai limpando o pool a cada ciclo (com um ",[299,3490,3491],{},"victim cache"," que dá a cada objeto um ciclo extra de sobrevida). É uma ferramenta para objetos caros e comprovadamente quentes, com o risco adicional de reter memória e de vazar dados entre usos se você esquecer de resetar o objeto.",[33,3494,3495,3501,3502,3504],{},[195,3496,3497,3500],{},[53,3498,3499],{},"unsafe.Pointer"," e stack allocation não se dão bem."," Como o compilador move valores entre stack e heap e o runtime copia stacks, código que faz aritmética de ponteiro fora das regras documentadas em ",[53,3503,3499],{}," pode quebrar quando uma alocação muda de lugar. As notas do Go 1.25 citam isso explicitamente ao introduzir mais alocações de slice na stack.",[217,3506,3508],{"id":3507},"mudanças-entre-versões","Mudanças entre versões",[33,3510,3511],{},"Os limites e otimizações citados aqui não são estáveis ao longo do tempo. As mudanças recentes que afetam este assunto:",[960,3513,3514,3524],{},[963,3515,3516],{},[966,3517,3518,3521],{},[969,3519,3520],{},"Versão",[969,3522,3523],{},"O que mudou",[976,3525,3526,3548,3563],{},[966,3527,3528,3531],{},[981,3529,3530],{},"Go 1.24",[981,3532,3533,3534,3537,3538,3541,3542,2018,3545,453],{},"O limite de declarações explícitas na stack (",[53,3535,3536],{},"MaxStackVarSize",") caiu de 10 MB para 128 KiB; o implícito seguiu em 64 KiB. Mudança interna, não citada nas release notes: dá para conferir comparando o ",[53,3539,3540],{},"cfg.go"," das tags ",[53,3543,3544],{},"go1.23.0",[53,3546,3547],{},"go1.24.0",[966,3549,3550,3553],{},[981,3551,3552],{},"Go 1.25",[981,3554,3555,3556,3559,3560,453],{},"O compilador passou a alocar o backing store de slices na stack em mais situações, incluindo ",[53,3557,3558],{},"make"," com tamanho variável. Pode ser desligado com ",[53,3561,3562],{},"-gcflags=all=-d=variablemakehash=n",[966,3564,3565,3567],{},[981,3566,210],{},[981,3568,3569],{},"O coletor Green Tea virou o padrão, com redução esperada de 10 a 40% no overhead de GC. Não muda o que escapa, mas muda o custo de escapar.",[33,3571,3572,3573,3576],{},"Vale destacar o último ponto. Green Tea não altera nenhuma decisão de escape analysis; ele reduz o custo de ",[299,3574,3575],{},"manter"," dados no heap, que é a parte do overhead que sobra depois da alocação. Isso empurra ainda mais a régua na direção de \"meça antes de otimizar\": uma micro-otimização que valia a pena em um projeto de dois anos atrás pode não valer mais, e a única forma de saber é medir na versão que você está usando.",[217,3578,3580],{"id":3579},"juntando-tudo-o-caminho-mental","Juntando tudo: o caminho mental",[33,3582,3583],{},"Se eu tivesse que resumir o raciocínio em uma sequência de perguntas:",[3585,3586,3587,3593,3610,3619,3638],"ol",{},[1404,3588,3589,3592],{},[195,3590,3591],{},"Esse código está em um caminho quente?"," Se não está, pare aqui. Escape analysis é assunto para o 1% do código que o profile aponta.",[1404,3594,3595,1692,3598,3600,3601,2992,3604,3606,3607,3609],{},[195,3596,3597],{},"Quantas alocações por operação?",[53,3599,1699],{}," primeiro, ",[53,3602,3603],{},"pprof",[53,3605,3141],{}," depois. Só então ",[53,3608,581],{},", e só no pacote suspeito.",[1404,3611,3612,3615,3616,3618],{},[195,3613,3614],{},"A alocação é necessária ou acidental?"," Se o dado precisa sobreviver à chamada, ela é necessária e você está no lugar errado. Acidentais são quase sempre boxing em ",[53,3617,1732],{},", closures em goroutines ou um ponteiro entrando em uma estrutura de vida longa.",[1404,3620,3621,1692,3624,3627,3628,2711,3631,3633,3634,3637],{},[195,3622,3623],{},"Existe uma API que evita o boxing?",[53,3625,3626],{},"slog.LogAttrs"," em vez de chaves soltas, ",[53,3629,3630],{},"strconv.Append*",[53,3632,2057],{},", um buffer local em vez de um ",[53,3635,3636],{},"io.Writer"," genérico.",[1404,3639,3640,3649],{},[195,3641,3642,3643,3645,3646,3648],{},"A mudança melhorou ",[53,3644,3469],{},", e não só ",[53,3647,3042],{},"?"," Se não melhorou, reverta e mantenha a versão legível.",[33,3651,3652,3653,3655],{},"O que eu levaria deste artigo, no fim, é uma inversão de perspectiva. Escape analysis não é uma otimização que você aplica; é uma garantia de correção que o compilador te dá de graça, e que por acaso também é rápida na maior parte das vezes. Retornar ",[53,3654,1488],{}," em Go é seguro pelo mesmo motivo pelo qual é eficiente: o compilador entende o tempo de vida dos seus dados melhor do que a sintaxe do seu código consegue expressar. Da mesma forma que os zero values tiraram de você a obrigação de lembrar de inicializar, o escape analysis tira a obrigação de decidir onde as coisas moram. Nos dois casos, o ganho é o mesmo: menos decisões para errar.",[217,3657,3659],{"id":3658},"referências","Referências",[1401,3661,3662,3669,3677,3694,3704,3717,3732,3754,3765,3773,3793,3808,3818],{},[1404,3663,3664,3668],{},[37,3665,3667],{"href":294,"rel":3666},[296],"Go FAQ — How do I know whether a variable is allocated on the heap or the stack?"," — a posição oficial sobre o assunto, e o critério que o compilador usa.",[1404,3670,3671,3676],{},[37,3672,3674],{"href":330,"rel":3673},[296],[53,3675,334],{}," — o comentário no topo do arquivo descreve as duas invariantes, o grafo de fluxo de dados e as parameter tags.",[1404,3678,3679,3686,3687,3689,3690,3693],{},[37,3680,3683],{"href":3681,"rel":3682},"https:\u002F\u002Fgithub.com\u002Fgolang\u002Fgo\u002Fblob\u002Fmaster\u002Fsrc\u002Fcmd\u002Fcompile\u002Finternal\u002Fir\u002Fcfg.go",[296],[53,3684,3685],{},"cmd\u002Fcompile\u002Finternal\u002Fir\u002Fcfg.go"," — os limites ",[53,3688,3536],{}," (128 KiB) e ",[53,3691,3692],{},"MaxImplicitStackVarSize"," (64 KiB).",[1404,3695,3696,3703],{},[37,3697,3700],{"href":3698,"rel":3699},"https:\u002F\u002Fgithub.com\u002Fgolang\u002Fgo\u002Fblob\u002Fmaster\u002Fsrc\u002Fcmd\u002Fcompile\u002Finternal\u002Fgc\u002Fmain.go",[296],[53,3701,3702],{},"cmd\u002Fcompile\u002Finternal\u002Fgc\u002Fmain.go"," — a ordem das etapas do compilador; devirtualização e inlining rodam antes da escape analysis.",[1404,3705,3706,3713,3714,3716],{},[37,3707,3710],{"href":3708,"rel":3709},"https:\u002F\u002Fgithub.com\u002Fgolang\u002Fgo\u002Fblob\u002Fmaster\u002Fsrc\u002Fruntime\u002Fstack.go",[296],[53,3711,3712],{},"runtime\u002Fstack.go"," — ",[53,3715,256],{},", o tamanho inicial de 2 KiB das stacks de goroutine e o mecanismo de crescimento por cópia.",[1404,3718,3719,3713,3725,3728,3729,3731],{},[37,3720,3723],{"href":3721,"rel":3722},"https:\u002F\u002Fgithub.com\u002Fgolang\u002Fgo\u002Fblob\u002Fmaster\u002Fsrc\u002Fruntime\u002Fiface.go",[296],[53,3724,2050],{},[53,3726,3727],{},"convT64"," e o array ",[53,3730,2046],{},", que evita a alocação ao empacotar inteiros menores que 256 em uma interface.",[1404,3733,3734,2018,3743,3750,3751,3753],{},[37,3735,3738,3740,3741],{"href":3736,"rel":3737},"https:\u002F\u002Fgithub.com\u002Fgolang\u002Fgo\u002Fblob\u002Fgo1.23.0\u002Fsrc\u002Fcmd\u002Fcompile\u002Finternal\u002Fir\u002Fcfg.go",[296],[53,3739,3540],{}," na tag ",[53,3742,3544],{},[37,3744,3747,3748],{"href":3745,"rel":3746},"https:\u002F\u002Fgithub.com\u002Fgolang\u002Fgo\u002Fblob\u002Fgo1.24.0\u002Fsrc\u002Fcmd\u002Fcompile\u002Finternal\u002Fir\u002Fcfg.go",[296],"na tag ",[53,3749,3547],{}," — a comparação que mostra ",[53,3752,3536],{}," indo de 10 MB para 128 KiB.",[1404,3755,3756,3761,3762,453],{},[37,3757,3760],{"href":3758,"rel":3759},"https:\u002F\u002Fgo.dev\u002Fdoc\u002Fgo1.25#compiler",[296],"Go 1.25 Release Notes — Compiler"," — alocação na stack do backing store de slices em mais situações, e a flag ",[53,3763,3764],{},"variablemakehash",[1404,3766,3767,3772],{},[37,3768,3771],{"href":3769,"rel":3770},"https:\u002F\u002Fgo.dev\u002Fdoc\u002Fgo1.26",[296],"Go 1.26 Release Notes — Green Tea GC"," — o novo coletor como padrão e o impacto esperado no overhead de GC.",[1404,3774,3775,3781,3782,1025,3784,3786,3787,3790,3791,453],{},[37,3776,3779],{"href":3777,"rel":3778},"https:\u002F\u002Fpkg.go.dev\u002Fcmd\u002Fgo#hdr-Compile_packages_and_dependencies",[296],[53,3780,1032],{}," — a sintaxe ",[53,3783,1024],{},[53,3785,1028],{}," e por que ",[53,3788,3789],{},"all=-m"," é diferente de ",[53,3792,587],{},[1404,3794,3795,3803,3804,3807],{},[37,3796,3799,3802],{"href":3797,"rel":3798},"https:\u002F\u002Fpkg.go.dev\u002Flog\u002Fslog#hdr-Performance_considerations",[296],[53,3800,3801],{},"log\u002Fslog"," — Performance considerations"," — a recomendação oficial de ",[53,3805,3806],{},"Logger.LogAttrs"," para evitar alocações, e outras dicas de custo em logging.",[1404,3809,3810,3817],{},[37,3811,3814,3815],{"href":3812,"rel":3813},"https:\u002F\u002Fpkg.go.dev\u002Funsafe#Pointer",[296],"Documentação do ",[53,3816,3499],{}," — os padrões válidos de conversão, e por que sair deles quebra na presença de stack copying.",[1404,3819,3820,3825,3826,3828],{},[37,3821,3824],{"href":3822,"rel":3823},"https:\u002F\u002Fgo.dev\u002Fblog\u002Fpprof",[296],"Profiling Go Programs"," — o fluxo de trabalho com ",[53,3827,3603],{}," no blog oficial.",[3830,3831,3832],"style",{},"html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}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 .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .swQdS, html code.shiki .swQdS{--shiki-light:#E53935;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":51,"searchDepth":87,"depth":87,"links":3834},[3835,3836,3837,3841,3848,3849,3850,3851,3852],{"id":219,"depth":87,"text":220},{"id":260,"depth":87,"text":261},{"id":577,"depth":87,"text":3838,"children":3839},"Parte 2: enxergando a decisão com -gcflags=-m",[3840],{"id":1039,"depth":107,"text":1040},{"id":1474,"depth":87,"text":1475,"children":3842},[3843,3844,3845,3846,3847],{"id":1481,"depth":107,"text":1482},{"id":1722,"depth":107,"text":1723},{"id":2369,"depth":107,"text":2370},{"id":2396,"depth":107,"text":2397},{"id":2724,"depth":107,"text":2725},{"id":3021,"depth":87,"text":3022},{"id":3169,"depth":87,"text":3170},{"id":3507,"depth":87,"text":3508},{"id":3579,"depth":87,"text":3580},{"id":3658,"depth":87,"text":3659},"2026-05-27","Como o compilador do Go decide se um valor fica na stack ou vai para o heap: o que é escape analysis, como ler a saída do -gcflags=-m, o que faz um valor escapar e o que realmente vale otimizar.","md","https:\u002F\u002Fencrypted-tbn0.gstatic.com\u002Fimages?q=tbn:ANd9GcQvXqKsNKgbxpINxN06GeyY8UPggpH6RUlMZtcJwYptYOPdaD3k4-nHOzd5&s=10",{},{"title":14,"description":3854},"uHKwFeKldxj4L9ChvsaJyRZv7SRBQJeRtPTXEarpOtM",1786023409655]