<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Yuri Matheus</title>
    <description>The latest articles on Forem by Yuri Matheus (@oyurimatheus).</description>
    <link>https://forem.com/oyurimatheus</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F337339%2F80a8677e-e4da-4389-9ea8-64ef0330d9e4.jpeg</url>
      <title>Forem: Yuri Matheus</title>
      <link>https://forem.com/oyurimatheus</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/oyurimatheus"/>
    <language>en</language>
    <item>
      <title>Uma introdução a JVM</title>
      <dc:creator>Yuri Matheus</dc:creator>
      <pubDate>Tue, 23 May 2023 14:05:25 +0000</pubDate>
      <link>https://forem.com/oyurimatheus/uma-introducao-a-jvm-3ie</link>
      <guid>https://forem.com/oyurimatheus/uma-introducao-a-jvm-3ie</guid>
      <description>&lt;p&gt;Uma das coisas que fez o Java ganhar popularidade foi o uso de uma máquina virtual para executar seu código. Todo código Java, antes de rodar de fato, é compilado para uma linguagem chamada de Bytecode que é interpretada na &lt;strong&gt;M&lt;/strong&gt;áquina &lt;strong&gt;V&lt;/strong&gt;irtual &lt;strong&gt;J&lt;/strong&gt;ava, ou JVM para os íntimos. &lt;/p&gt;

&lt;p&gt;Dessa forma, conseguimos escrever um único código e pedir para ele ser executado em qualquer sistema que tenha a JVM instalada. Foi nesse contexto que surge a expressão &lt;strong&gt;Write once, run everywhere&lt;/strong&gt;, ou Escreva uma única vez e rode em qualquer lugar.&lt;/p&gt;

&lt;p&gt;A JVM vai cuidar de abstrair o sistema operacional (SO) das pessoas que escrevem um código que rode nela. Dessa forma, &lt;a href="https://man.archlinux.org/man/syscalls.2.en" rel="noopener noreferrer"&gt;system calls&lt;/a&gt;, gerenciamento de memória, threads e processos, entre diversas outras coisas são abstraídas para nós que escrevemos o código.&lt;/p&gt;

&lt;p&gt;Por não interpretar código Java, mas sim um bytecode gerado no processo de compilação, a JVM permite com que nós utilizemos a linguagem de programação que achemos melhor. Clojure, Kotlin, Scala e Groovy são exemplos de linguagem que roda na JVM. &lt;/p&gt;

&lt;p&gt;Quando compiladas, essas linguagens vão para uma estrutura de bytecode que consegue ser interpretada na JVM. Vamos pensar em um simples &lt;code&gt;Olá, mundo!&lt;/code&gt; escrito em Kotlin e em Java.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Java&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OlaMundo&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

   &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
       &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Olá, mundo!"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
   &lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Bytecode do código Java gerado:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Compiled from "OlaMundo.java"
public class OlaMundo {
  public OlaMundo();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."&amp;lt;init&amp;gt;":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #13                 // String Olá, mundo!
       5: invokevirtual #15                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Kotlin&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Olá, mundo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Bytecode do código Kotlin gerado:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Compiled from "olamundo.kt"
public final class OlamundoKt {
  public static final void main();
    Code:
       0: ldc           #11                 // String Olá, mundo
       2: astore_0
       3: iconst_0
       4: istore_1
       5: getstatic     #17                 // Field java/lang/System.out:Ljava/io/PrintStream;
       8: aload_0
       9: invokevirtual #23                 // Method java/io/PrintStream.print:(Ljava/lang/Object;)V
      12: return

  public static void main(java.lang.String[]);
    Code:
       0: invokestatic  #9                  // Method main:()V
       3: return
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note que o &lt;strong&gt;bytecode&lt;/strong&gt; do código Kotlin é bem similar ao do código Java. Mas, como esses bytecodes são carregados na JVM?&lt;/p&gt;

&lt;h2&gt;
  
  
  O processo de interpretação da JVM
&lt;/h2&gt;

&lt;p&gt;Todo arquivo compilado para &lt;strong&gt;bytecode Java&lt;/strong&gt; ganha um &lt;code&gt;.class&lt;/code&gt; ao final de seu nome. Esse sufixo é uma forma de informar a JVM sobre que aquela é uma classe Java pronta para ser carregada. Esse processo é chamado &lt;strong&gt;Loading&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Esse Loading é efetuado por um &lt;strong&gt;Class Loader&lt;/strong&gt; que, basicamente, vai gerar um objeto em memória do tipo &lt;code&gt;Class&lt;/code&gt; (instância de &lt;code&gt;java.lang.Class&lt;/code&gt;) no qual carregará as metainformações daquela classe, como nome, referências das superclasses, nomes de métodos, etc.&lt;/p&gt;

&lt;p&gt;A JVM tem diversos &lt;code&gt;Class Loader&lt;/code&gt; e segue uma ordem específica para a fim de promover uma certa segurança no código. Por exemplo, se criarmos uma classe chamada &lt;code&gt;String&lt;/code&gt;, como garantir que outras partes do código estão usando de fato a classe &lt;code&gt;java.lang.String&lt;/code&gt; e não a classe que a gente criou?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Para saber mais sobre os class loader, vale uma olhada na especificação da JVM &lt;a href="https://docs.oracle.com/javase/specs/jvms/se19/html/jvms-5.html#jvms-5.3.1" rel="noopener noreferrer"&gt;https://docs.oracle.com/javase/specs/jvms/se19/html/jvms-5.html#jvms-5.3.1&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Após a etapa de Loading, começa a etapa de &lt;strong&gt;Linking&lt;/strong&gt;. Nesta fase, é verificado se o bytecode está bem formatado (etapa de verificação), alocação de espaço para as variáveis estáticas (etapa de preparação) e, caso necessário, uma resolução das referências da classe e do objeto em questão (etapa de resolução).&lt;/p&gt;

&lt;p&gt;Essa etapa de resolução pode acontecer antes ou depois da próxima etapa que é a etapa de inicialização. Essa última etapa é a que de fato vai inicializar as variáveis do objeto e deixá-lo pronto para uso. &lt;/p&gt;

&lt;p&gt;No diagrama abaixo, tem uma simples ilustração dessas fases:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftk0q8f5wgyj8wena9bwv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftk0q8f5wgyj8wena9bwv.png" alt="Etapas para carregar um objeto na JVM"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Agora sabemos o que acontece cada vez que um objeto é criado na JVM, mas o que acontece quando não precisamos mais desse objeto?&lt;/p&gt;

&lt;h2&gt;
  
  
  Um pouco sobre o modelo de memória
&lt;/h2&gt;

&lt;p&gt;Em linguagens como C, precisamos ativamente alocar e desalocar espaços de memória quando necessário. Se nesse processo de alocação, a aplicação pedir um espaço de memória para o sistema operacional e não conseguir obter o espaço necessário a aplicação apresentará um erro.&lt;/p&gt;

&lt;p&gt;No caso de um código que roda na JVM, todo esse processo de alocação de memória é transparente. A JVM cria espaços virtuais de memória no qual a aplicação, isto é, o código Java, Kotlin, etc, interage com essa memória virtual, ao invés da memória real do computador.&lt;/p&gt;

&lt;p&gt;Existem, basicamente, três espaços de memória na JVM: &lt;code&gt;stack&lt;/code&gt;, &lt;code&gt;heap&lt;/code&gt; e a &lt;code&gt;method area&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A &lt;code&gt;stack&lt;/code&gt; é um espaço único por thread. Nele são registrado variáveis de tipos primitivos, resultados parciais de execuções, retornos de métodos, entre outros tipos de dados e metadados. Segundo a especificação da JVM, &lt;a href="https://docs.oracle.com/javase/specs/jvms/se19/html/jvms-2.html#jvms-2.6" rel="noopener noreferrer"&gt;esses tipos de dados são armazenados em &lt;code&gt;frames&lt;/code&gt;&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Na &lt;code&gt;heap&lt;/code&gt; são armazenados os objetos de fato. Cada vez que damos um &lt;code&gt;new Obj();&lt;/code&gt;, este novo objeto é armazenado na heap e uma referência a ele fica em algum frame da stack. Por conta dessa natureza, a &lt;code&gt;heap&lt;/code&gt; é um espaço de memória bem maior que a &lt;code&gt;stack&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Quando esses objetos que estão na &lt;code&gt;heap&lt;/code&gt; perdem a sua referência, a JVM automagicamente consegue identificar esse objeto como lixo e coletá-lo da memória. Para isso ela utiliza de algoritmos de Garbage Collection, ou GC, para os íntimos.&lt;/p&gt;

&lt;p&gt;Cada implementação da JVM pode ter um, ou mais, algoritmos de GC, com otimizações diferentes. A especificação apenas define que objetos na &lt;code&gt;heap&lt;/code&gt; devem ser recolhidos por esse sistema.&lt;/p&gt;

&lt;p&gt;Existem diversos algoritmos de GC e diversas formas de otimizá-los. &lt;strong&gt;Cada algoritmo conta com bônus e ônus&lt;/strong&gt; e pode se aplicar a aplicações e casos de uso distintos. Como é um assunto complexo e extenso, foge um pouco do escopo deste post. Contudo, o Yuri do futuro deve escrever alguns posts sobre o assunto. &lt;/p&gt;

&lt;p&gt;Para agora, basta com que saibamos que esses algoritmos existem e, por causa deles, não precisamos nos preocupar tanto em gerenciar a memória das nossas aplicações que rodam sob a JVM&lt;/p&gt;

&lt;h2&gt;
  
  
  Conhecendo o JIT
&lt;/h2&gt;

&lt;p&gt;O código interpretado pela JVM é, em geral, mais lento que um código de máquina, isto é, um código que roda diretamente no computador, sem um intermediário (como é o caso da máquina virtual). &lt;br&gt;
Pensando em resolver esse problema, a especificação da JVM apresentou o conceito de JIT, ou &lt;em&gt;just-in-time compiler&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;O JIT nada mais é do que outro compilador que é executado pela JVM e transforma o bytecode Java em código nativo. Além de compilar para código nativo, a JVM também pode fazer outras melhorias de performance nos código, a fim de ter uma execução mais rápida. &lt;/p&gt;

&lt;p&gt;Cada implementação de JVM tem seu próprio algoritmo e formas de compilar e otimizar esse código, mas basicamente ela vai se dar através de trechos de código que são mais executados em determinado período de tempo. &lt;/p&gt;

&lt;h2&gt;
  
  
  Uma JVM para cada uso
&lt;/h2&gt;

&lt;p&gt;A JVM é um grande trunfo para quem programa com Java ou outras linguagens suportadas por ela. Ela tem uma boa especificação e diversas implementações para casos de uso e necessidades distintas. &lt;/p&gt;

&lt;p&gt;Entender mais dessa ferramenta nos ajuda no processo de resolução de erros (troubleshooting), a melhorar a performance e arquitetar aplicações mais robustas e resilientes.&lt;/p&gt;

</description>
      <category>java</category>
      <category>jvm</category>
    </item>
  </channel>
</rss>
