Desvantagens do gerenciamento de memória com escopo definido


38

Eu realmente gosto do gerenciamento de memória com base em escopo (SBMM), ou RAII , pois é mais comum (confusamente?) Referido pela comunidade C ++. Até onde eu sei, exceto C ++ (e C), não há outra linguagem convencional em uso hoje que faça do SBMM / RAII seu principal mecanismo de gerenciamento de memória e, em vez disso, eles preferem usar a coleta de lixo (GC).

Acho isso bastante confuso, pois

  1. O SBMM torna os programas mais determinísticos (você pode dizer exatamente quando um objeto é destruído);
  2. nas linguagens que usam o GC, muitas vezes é necessário o gerenciamento manual de recursos (consulte o fechamento de arquivos em Java, por exemplo), que derruba parcialmente o objetivo do GC e também é passível de erros;
  3. a memória heap também pode (muito elegantemente, imo) ser vinculada ao escopo (veja std::shared_ptrem C ++).

Por que o SBMM não é mais amplamente usado? Quais são as suas desvantagens?


1
Algumas desvantagens (especialmente em relação à velocidade) são discutidas na Wikipedia: en.wikipedia.org/wiki/…
Philipp

2
O problema manual de gerenciamento de recursos do Java é um efeito colateral de não garantir que o finalize()método de um objeto seja chamado antes da coleta de lixo. Com efeito, isso cria a mesma classe de problema que a coleta de lixo deve resolver.
Blrfl

7
@Blrfl Nonsense. O gerenciamento de recursos "manual" (para outros recursos que não a memória, obviamente) seria preferível, mesmo que esse "problema" não existisse, porque o GC pode ser executado por um período muito longo após o recurso não ser utilizado ou mesmo nem ser executado. Isso não é problema para a memória , e o gerenciamento de memória é tudo o que a coleta de lixo deve resolver.

4
btw. Eu gosto de me referir a ele como SBRM, já que você pode usar o mesmo mecanismo para gerenciar recursos em geral, não apenas memória.
PlasmaHH

Respostas:


27

Vamos começar postulando que a memória é de longe (dezenas, centenas ou até milhares de vezes) mais comum do que todos os outros recursos combinados. Cada variável, objeto, membro do objeto precisa de alguma memória alocada e liberada posteriormente. Para cada arquivo aberto, você cria dezenas a milhões de objetos para armazenar os dados extraídos do arquivo. Todo fluxo TCP acompanha um número ilimitado de cadeias de bytes temporárias criadas para serem gravadas no fluxo. Estamos na mesma página aqui? Ótimo.

Para que o RAII funcione (mesmo que você tenha ponteiros inteligentes prontos para cada caso de uso sob o sol), é necessário ter a propriedade correta. Você precisa analisar quem deve ser o proprietário desse ou daquele objeto, quem não deve e quando a propriedade deve ser transferida de A para B. Claro, você pode usar a propriedade compartilhada para tudo , mas depois emula um GC por meio de ponteiros inteligentes. Nesse ponto, fica muito mais fácil e rápido criar o GC no idioma.

A coleta de lixo libera você dessa preocupação com a memória de longe o recurso mais comumente usado. Claro, você ainda precisa tomar a mesma decisão para outros recursos, mas esses são muito menos comuns (veja acima), e a propriedade complicada (por exemplo, compartilhada) também é menos comum. A carga mental é reduzida significativamente.

Agora, você cita algumas desvantagens de tornar todos os valores coletados como lixo. No entanto, a integração de GC e tipos de valor com segurança de memória com RAII em um idioma é extremamente difícil, portanto, talvez seja melhor migrar essas compensações por outros meios?

A perda de determinismo acaba por não ser tão ruim na prática, porque afeta apenas a vida determinística do objeto . Conforme descrito no próximo parágrafo, a maioria dos recursos (além da memória, que é abundante e pode ser reciclada preguiçosamente) não está vinculada ao tempo de vida do objeto nessas linguagens. Existem alguns outros casos de uso, mas eles são raros na minha experiência.

Atualmente, seu segundo ponto, gerenciamento manual de recursos, é abordado por meio de uma declaração que executa a limpeza com base no escopo, mas não acopla essa limpeza ao tempo de vida do objeto (portanto, não interage com o GC e a segurança da memória). Isso está usingem C #, withem Python, trycom recursos nas versões recentes do Java.


1
Isso não explica por que modelos não determinísticos como o GC devem ser superiores aos modelos determinísticos. usingdeclarações são possíveis apenas localmente. É impossível limpar os recursos mantidos nas variáveis ​​membros dessa maneira.
Philipp

8
@Philipp A propriedade compartilhada de tudo é um GC, apenas muito pobre. Se você usar "propriedade compartilhada" para implicar uma contagem de ref, por favor, diga-o, mas mantenha a discussão sobre ciclos nos comentários da resposta de amon. Também não tenho certeza se a contagem de ref é determinística no sentido em que o OP está interessado (os objetos são liberados o mais cedo possível, descontando ciclos, mas muitas vezes você não pode dizer quando é olhando o programa). Além disso, a contagem de referências para tudo é lenta, muito mais lenta que um GC de rastreamento moderno.

16
usingé uma piada comparada à RAII, só para você saber.
23414 DeadMG

3
@ Philipp, descreva sua métrica para "superior". É verdade que o gerenciamento manual de memória é mais rápido no tempo de execução para lidar com o gerenciamento de memória. No entanto, o custo do software não pode ser julgado apenas pelo tempo de CPU gasto apenas no gerenciamento de memória.
ArTs 10/03

2
@ArTs: eu não necessariamente concordaria com isso. O RAII vem com o requisito de que um objeto deve ser destruído ao sair do escopo. Um loop, então, é necessário para fazer n destruções de objetos. Sob um GC geracional moderno, essas destruições podem ser adiadas até o final do loop, ou até mais tarde, e executar apenas uma operação para destruir centenas de iterações no valor de memória. Um GC pode ser muito, muito rápido, nos seus bons casos.
Phoshi

14

O RAII também segue do gerenciamento automático de memória de contagem de referência, por exemplo, conforme usado pelo Perl. Embora a contagem de referência seja fácil de implementar, determinística e de alto desempenho, ela não pode lidar com referências circulares (elas causam um vazamento), razão pela qual não é comumente usada.

Os idiomas coletados pelo lixo não podem usar RAII diretamente, mas geralmente oferecem sintaxe com um efeito equivalente. Em Java, temos a instrução try-with-ressource

try (BufferedReader br = new BufferedReader(new FileReader(path))) { ... }

que chama automaticamente .close()o recurso na saída do bloco. O C # possui a IDisposableinterface, que permite .Dispose()ser chamada ao sair de uma using (...) { ... }instrução. Python tem a withdeclaração:

with open(filename) as f:
    ...

que funciona de maneira semelhante. Em uma análise interessante, o método de abertura de arquivo do Ruby recebe um retorno de chamada. Depois que o retorno de chamada foi executado, o arquivo é fechado.

File.open(name, mode) do |f|
    ...
end

Eu acho que o Node.js usa a mesma estratégia.


4
O uso de funções de ordem superior para gerenciamento de recursos remonta muito antes do Ruby. No Lisps, é bastante comum ter, digamos, with-open-filehandlefunções que abrem o arquivo, produzem uma função e, ao retornar a função, fecham o arquivo novamente.
Jörg W Mittag

4
O argumento de referência cíclico é bastante comum, mas qual a sua importância? As referências cíclicas podem ser atenuadas usando ponteiros fracos se a propriedade for clara.
Philipp

2
@ Philipp Quando você usa a contagem de referências, a propriedade geralmente não é clara. Além disso, esta resposta fala sobre idiomas que usam a contagem de referência exclusiva e automaticamente, portanto, referências fracas não existem ou são muito mais difíceis de usar do que referências fortes.

3
As estruturas de dados cíclicos do Philipp são muito raras, a menos que você esteja trabalhando com gráficos complicados. Ponteiros fracos não ajudam em um gráfico de objeto cíclico geral, embora ajudem em casos mais comuns, como ponteiros pai em uma árvore. Uma boa solução alternativa é manter um objeto de contexto que represente a referência ao gráfico inteiro e gerencie a destruição. A recontagem não é um negociador, mas exige que o programador esteja ciente de suas restrições. Ou seja, tem um custo cognitivo um pouco maior que o GC.
amon

1
Uma razão importante pela qual a contagem de referências raramente é usada é que geralmente é mais lenta que a GC, apesar de sua simplicidade.
Rufflewind

14

Na minha opinião, a vantagem mais convincente da coleta de lixo é que ela permite a composição . A correção do gerenciamento de memória é uma propriedade local no ambiente de coleta de lixo. Você pode examinar cada parte isoladamente e determinar se ela pode vazar memória. Combine qualquer número de peças com correção de memória e elas permanecerão corretas.

Quando você conta com a contagem de referências, perde essa propriedade. Se seu aplicativo pode vazar memória torna-se uma propriedade global de todo o aplicativo com contagem de referência. Toda nova interação entre partes tem a possibilidade de usar a propriedade errada e interromper o gerenciamento de memória.

Tem um efeito muito visível no design de programas nas diferentes linguagens. Os programas nas linguagens GC tendem a ser um pouco mais de sopas de objetos com muitas interações, enquanto nas linguagens sem GC costumamos preferir partes estruturadas com interações estritamente controladas e limitadas entre elas.


1
A correção é apenas compostável se os objetos mantiverem referências apenas em seu próprio nome, e não em nome dos destinos dessas referências. Quando coisas como notificações entram no mix (Bob mantém uma referência a Joe porque Joe pediu a Bob para notificá-lo quando algo aconteceu e Bob prometeu fazê-lo, mas Bob não se importa com Joe), a correção do GC geralmente requer gerenciamento de recursos com escopo [implementado manualmente em muitos casos, já que os sistemas de GC não possuem a automação do C ++].
Supercat

@supercat: "A correção do GC geralmente requer gerenciamento de recursos com escopo definido". Eh? O escopo existe apenas no código-fonte e o GC existe apenas no tempo de execução (e, portanto, é completamente alheio à existência do escopo).
quer

@ JonHarrop: Eu estava usando o termo "escopo" no mesmo sentido que um ponteiro com escopo em C ++ [a vida útil do objeto deve ser a do contêiner que o contém], pois esse é o uso implícito na pergunta original. O que quero dizer é que os objetos criam referências potencialmente duradouras para propósitos como receber eventos podem não ser composíveis em um sistema puramente GC. Para correção, certas referências precisam ser fortes e certas referências precisam ser fracas, e quais referências precisam ser quais dependerão de como um objeto é usado. Por exemplo ...
supercat 13/01

... suponha que os objetos Fred e Barney se inscrevam para notificação sempre que algo em um determinado diretório for modificado. O manipulador de Fred não faz nada além de incrementar um contador cujo valor ele pode relatar mediante solicitação, mas para o qual não tem outro uso. O manipulador de Barney abrirá uma nova janela se um determinado arquivo for modificado. Para ser correto, Fred deve ser inscrito com um evento fraco, mas o de Barney deve ser forte, mas o objeto timer não terá como saber disso.
Supercat

@supercat: Certo. Eu não diria que isso acontece "frequentemente". Só o encontrei uma vez em 30 anos de programação.
precisa saber é o seguinte

7

Os fechamentos são uma característica essencial de praticamente todas as línguas modernas. Eles são muito fáceis de implementar com o GC e muito difíceis (embora não impossíveis) de acertar com o RAII, pois uma das principais características é que eles permitem abstrair durante a vida útil de suas variáveis!

O C ++ só os obteve 40 anos depois que todo mundo fez, e foi preciso muito trabalho duro de muitas pessoas inteligentes para acertá-los. Por outro lado, muitas linguagens de script projetadas e implementadas por pessoas com zero conhecimento em design e implementação de linguagens de programação as possuem.


9
Eu não acho que fechamentos em C ++ sejam um bom exemplo. As lambdas no C ++ 11 são apenas açúcar sintático para classes de functor (que datam significativamente do C ++ 11) e são igualmente inseguras de memória: se você capturar algo por referência e chamar o fechamento depois que esse item estiver morto, você simplesmente obtém o UB, como manter uma referência por mais tempo que o válido. O fato de terem aparecido 40 anos atrasado deve-se ao reconhecimento tardio da PF, não ao descobrir como torná-las seguras. E embora projetá-los certamente tenha sido uma tarefa enorme, duvido que a maior parte do esforço tenha sido levada em consideração ao longo da vida.

Eu concordo com o delnan: o C ++ não acertou os fechamentos: você precisa programá-los com muito cuidado se não quiser obter um dump principal ao invocá-los.
Giorgio

2
@ delnan: os lambda de captura por referência têm intencionalmente essa [&]sintaxe. Qualquer programador C ++ já associa o &sinal a referências e conhece referências obsoletas.
MSalters

2
@MSalters Qual é o seu ponto? Eu mesmo desenhei a conexão de referência. Eu não disse que lambdas C ++ são excepcionalmente inseguras, eu disse que elas são exatamente tão inseguras quanto as referências. Eu não argumentei que as lambdas do C ++ são ruins, argumentei contra a afirmação desta resposta (que o C ++ foi encerrado muito tarde porque eles tiveram que descobrir como fazer isso corretamente).

5
  1. O SBMM torna os programas mais determinísticos (você pode dizer exatamente quando um objeto é destruído);

Para a maioria dos programadores, o SO não é determinístico, seu alocador de memória é não determinístico e a maioria dos programas que eles escrevem são simultâneos e, portanto, inerentemente não determinísticos. Adicionar a restrição de que um destruidor é chamado exatamente no final do escopo, e não um pouco antes ou um pouco depois, não é um benefício prático significativo para a grande maioria dos programadores.

  1. nas linguagens que usam o GC, muitas vezes é necessário o gerenciamento manual de recursos (consulte o fechamento de arquivos em Java, por exemplo), que derruba parcialmente o objetivo do GC e também é passível de erros;

Veja usingem C # e useem F #.

  1. a memória heap também pode (muito elegantemente, imo) ser vinculada ao escopo (consulte std :: shared_ptr em C ++).

Em outras palavras, você pode pegar o heap, que é uma solução de uso geral, e alterá-lo para funcionar apenas em um caso específico que seja seriamente limitador. Isso é verdade, é claro, mas é inútil.

Por que o SBMM não é mais amplamente usado? Quais são as suas desvantagens?

O SBMM limita o que você pode fazer:

  1. O SBMM cria o problema de funarg para cima com fechamentos lexicais de primeira classe, e é por isso que os fechamentos são populares e fáceis de usar em linguagens como C #, mas raros e complicados em C ++. Observe que há uma tendência geral para o uso de construções funcionais na programação.

  2. O SBMM requer destruidores e eles impedem chamadas finais adicionando mais trabalho a ser feito antes que uma função possa retornar. As chamadas de cauda são úteis para máquinas de estado extensíveis e são fornecidas por coisas como o .NET.

  3. Algumas estruturas de dados e algoritmos são notoriamente difíceis de implementar usando o SBMM. Basicamente em qualquer lugar que os ciclos ocorram naturalmente. Mais notavelmente algoritmos gráficos. Você efetivamente acaba escrevendo seu próprio GC.

  4. A programação simultânea é mais difícil porque o fluxo de controle e, portanto, a vida útil do objeto são inerentemente não determinísticos aqui. As soluções práticas nos sistemas de passagem de mensagens tendem a ser cópias profundas das mensagens e o uso de vidas excessivamente longas.

  5. O SBMM mantém os objetos ativos até o final de seu escopo no código-fonte, que geralmente é maior que o necessário e pode ser muito maior que o necessário. Isso aumenta a quantidade de lixo flutuante (objetos inacessíveis aguardando para serem reciclados). Por outro lado, o rastreamento da coleta de lixo tende a liberar objetos logo após a última referência a eles desaparecer, o que pode ser muito mais rápido. Consulte Mitos sobre gerenciamento de memória: rapidez .

O SBMM é tão limitador que os programadores precisam de uma rota de fuga para situações em que não é possível aninhar a vida útil. No C ++, shared_ptroferece uma rota de fuga, mas pode ser ~ 10x mais lenta que rastrear a coleta de lixo . Portanto, o uso do SBMM em vez do GC colocaria a maioria das pessoas erradas na maioria das vezes. Isso não quer dizer, no entanto, que seja inútil. O SBMM ainda é valioso no contexto de sistemas e programação incorporada, onde os recursos são limitados.

No FWIW, você pode conferir Forth e Ada e ler o trabalho de Nicolas Wirth.


1
Se você disser quais partes, poderei elaborar ou citar artigos.
quer

2
Quão relevante é ser 10 vezes mais lento em alguns casos de uso raros do que ser onipresente em todos os casos de uso? O C ++ possui unique_ptr e, na maioria dos casos, é suficiente. Além disso, em vez de atacar o RAII através do C ++ (uma linguagem que muitos adoram odiar por ser uma língua arcaica), se você for atacar o RAII através do ataque a um idioma, tente um irmão mais novo da família RAII, Rust, por exemplo. Basicamente, Rust faz tudo certo que o C ++ deu errado, enquanto que o C ++ também acertou. Um 'uso' adicional fornece um conjunto muito limitado de casos de uso e ignora a composição.
user1703394

2
"Quão relevante é ser 10x mais lento em alguns casos de uso raros do que ser onipresente em todos os casos de uso?". Em primeiro lugar, esse é um argumento circular: shared_ptrsó é raro em C ++ porque é muito lento. Em segundo lugar, é uma comparação de maçãs e laranjas (como o artigo que citei já mostrou) porque shared_ptré muitas vezes mais lenta que um GC de produção. Em terceiro lugar, os GCs não são onipresentes e são evitados em softwares como o LMax e o mecanismo FIX da Rapid Addition.
quer

1
@ Jon Harrop, se você não me esclareceu, por favor. Que receita mágica você usou nesses 30 anos para mitigar os efeitos transitivos do uso de recursos profundos? Sem essa receita mágica depois de mais de 30 anos, eu só poderia concluir que você deve ter sido atribuída de forma incorreta ao ser mordida por outras causas.
user1703394

1
@ Jon Harrop, shared_ptr não é raro porque é lento, é raro porque, em um sistema decentemente projetado, a necessidade de 'propriedade compartilhada' é rara.
precisa saber é o seguinte

4

Olhando para algum índice de popularidade como o TIOBE (que é discutível, é claro, mas acho que seu tipo de pergunta pode ser usado), primeiro você vê que ~ 50% dos 20 principais são "linguagens de script" ou "dialetos SQL" ", onde a" facilidade de uso "e os meios de abstração têm muito mais importância do que o comportamento determinístico. Dos idiomas "compilados" restantes, existem cerca de 50% dos idiomas com SBMM e ~ 50% sem. Portanto, ao tirar as linguagens de script do seu cálculo, eu diria que sua suposição está errada, entre as linguagens compiladas as que possuem o SBMM são tão populares quanto as que não possuem.


1
Qual a diferença entre "facilidade de uso" e determinismo? Uma linguagem determinística não deve ser considerada mais fácil de usar do que uma linguagem não determinística?
Philipp

2
@ Philipp Apenas o que é determinístico ou não realmente importa. O tempo de vida do objeto não importa por si só (embora o C ++ e os amigos vinculem muitas coisas que importam ao objeto do tempo de vida, porque podem). Quando um objeto inacessível é liberado, não importa, porque, por definição, você não o está mais usando.

Mais uma coisa, várias "linguagens de script" como Perl e Python também usam a contagem de referência como principal meio de gerenciamento de memória.
Philipp

1
@ Philipp Pelo menos no mundo Python, isso é considerado um detalhe da implementação do CPython, não uma propriedade da linguagem (e praticamente todas as outras implementações evitam a recontagem). Além disso, eu argumentaria que a contagem de referência catch-all no opt-out com o GC do ciclo de backup não se qualifica como SBMM ou RAII. Na verdade, seria difícil encontrar proponentes de RAII que considerem esse estilo de gerenciamento de memória comparável ao RAII (principalmente porque não é, ciclos em qualquer lugar podem impedir a desalocação imediata em qualquer outro lugar do programa).

3

Uma grande vantagem de um sistema de GC que ninguém mencionou ainda é que é garantida uma referência em um sistema de GC para manter sua identidade enquanto existir . Se alguém chamar IDisposable.Dispose(.NET) ou AutoCloseable.Close(Java) um objeto enquanto existirem cópias da referência, essas cópias continuarão se referindo ao mesmo objeto. O objeto não será mais útil para nada, mas as tentativas de usá-lo terão um comportamento previsível controlado pelo próprio objeto. Por outro lado, em C ++, se o código chama deleteum objeto e depois tenta usá-lo, todo o estado do sistema fica totalmente indefinido.

Outra coisa importante a ser observada é que o gerenciamento de memória baseado em escopo funciona muito bem para objetos com propriedade claramente definida. Funciona muito menos bem e, às vezes, mal, com objetos que não possuem propriedade definida. Em geral, objetos mutáveis ​​devem ter proprietários, enquanto objetos imutáveis ​​não precisam, mas há um problema: é muito comum o código usar uma instância de tipos mutáveis ​​para armazenar dados imutáveis, garantindo que nenhuma referência seja exposta a eles. código que pode alterar a instância. Nesse cenário, instâncias da classe mutável podem ser compartilhadas entre vários objetos imutáveis ​​e, portanto, não possuem propriedade clara.


4
A propriedade a que você se refere no primeiro semestre é a segurança da memória. Embora um GC seja uma maneira muito fácil de obter segurança na memória, ele não é necessário: veja Rust para obter um exemplo bem-feito.

@ delnan: Quando visualizo o rust-lang.org, meu navegador não consegue navegar de maneira útil a partir daí; onde devo procurar mais informações? Minha impressão é que a segurança da memória sem GC impõe certas restrições às estruturas de dados que podem não se encaixar bem com todas as coisas que um aplicativo pode precisar fazer, mas ficaria feliz em provar que estou errado.
supercat

1
Não conheço um único (ou mesmo pequeno conjunto de) boas referências para isso; meu conhecimento sobre o Rust acumulou mais de um ano ou dois de leitura da lista de e-mails (e todo o material vinculado aos e-mails, incluindo vários tutoriais, posts em blogs de design de idiomas, problemas no github, ThisWeekInRust e mais). Para abordar brevemente sua impressão: Sim, cada construção segura (necessariamente) impõe restrições, mas para praticamente qualquer parte de código segura de memória, existe uma construção segura apropriada ou pode ser gravada. Os de longe os mais comuns já existem no idioma e no stdlib, todos os outros podem ser escritos no código do usuário.

@delnan: O Rust exige atualizações intertravadas nos balcões de referência ou possui outros meios para lidar com objetos imutáveis ​​(ou objetos mutáveis ​​imutáveis) que não possuem propriedade definida? Rust possui um conceito de ponteiros "de propriedade de objeto" e "não de propriedade"? Lembro-me de um artigo sobre ponteiros "Xonor", que discutia a idéia de objetos com uma única referência que os "possui" e outras referências que não; quando a referência de "possuir" sai do escopo, todas as referências não possuir se tornariam referências a objetos mortos, e seria identificáveis como tal ...
supercat

1
Não acho que os comentários do Stack Exchange sejam o meio certo para fazer um tour por um idioma. Se você ainda estiver interessado, pode ir diretamente à fonte (#rust IRC, lista de discussão sobre ferrugem etc.) e / ou entrar em contato com uma sala de bate-papo (você poderá criar uma).

-2

Primeiro, é muito importante perceber que igualar RAII a SBMM. ou mesmo para SBRM. Uma das qualidades mais essenciais (e menos conhecidas ou menos valorizadas) da RAII é o fato de tornar 'ser um recurso' uma propriedade que NÃO é transitiva para a composição.

A postagem do blog a seguir discute esse aspecto importante do RAII e o contrasta com o surgimento de recursos em idiomas GCed que usam GC não determinístico.

http://minorfs.wordpress.com/2011/04/29/why-garbage-collection-is-anti-productive/

É importante observar que, embora o RAII seja usado principalmente em C ++, o Python (finalmente a versão não baseada em VM) possui destruidores e GC determinístico que permitem que o RAII seja usado junto com o GC. Melhor dos dois mundos, se fosse.


1
-1 Esse é um dos piores artigos que eu já li.
quer

1
O problema nos idiomas não é que eles suportam o GC, mas abandonam o RAII. Não há razão para que uma linguagem / estrutura não possa suportar ambos.
supercat

1
@ Jon Harrop, você poderia elaborar. Das reivindicações feitas no artigo, há mesmo uma das 3 primeiras reivindicações que não se sustentam? Acho que você pode discordar da reivindicação de produtividade, mas as outras três reivindicações são absolutamente válidas. Mais importante ainda, o primeiro sobre a transitividade de ser um recurso.
user1703394

2
@ user1703394: Em primeiro lugar, o artigo inteiro é baseado em uma "linguagem GCed" simples, quando, na verdade, não tem nada a ver com coleta de lixo. Em segundo lugar, ele culpa a coleta de lixo quando, de fato, as falhas estão na programação orientada a objetos. Finalmente, seu argumento é 10 anos tarde demais. A grande maioria dos programadores já se modernizou para as linguagens de coleta de lixo precisamente porque elas oferecem uma produtividade muito maior.
quer

1
Seus exemplos concretos (RAM, identificadores de arquivos abertos, bloqueios, threads) são bastante reveladores. É difícil lembrar a última vez que tive que escrever um código que tratasse diretamente de qualquer um deles. Com a RAM, o GC automatiza tudo. Com identificadores de arquivo, escrevo código como File.ReadLines file |> Seq.lengthonde as abstrações lidam com o fechamento para mim. Bloqueios e threads que substituí pelo .NET Taske F # MailboxProcessor. Todo esse "explodimos a quantidade de gerenciamento manual de recursos" é apenas um completo disparate.
quer
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.