Ir para conteúdo
  • Cadastre-se

dev botao

Sped Contábil - Bloco I - Out of Memory


  • Este tópico foi criado há 339 dias atrás.
  • Talvez seja melhor você criar um NOVO TÓPICO do que postar uma resposta aqui.

Recommended Posts

Bom dia,

Caros colegas, gostaria de algumas sugestões. No  momento, estou gerando o Sped Contabil  de forma anual, onde o Registro I200/I250 possui em média 500 mil registros/Mes. No momento, ao gerar o registro ocorre a mensagem de 'Out of Memory'. Esta mensagem ocorre no momento em que estou preenchendo o componente.

Verificando outros tópicos semelhantes, ajustei a configuração de  LinhasBuffer para 300000 e não tive sucesso. Em casos de 200 mil registros/Mes funciona normalmente.

Não esta sendo utilizado o ClientDataSet para buscar os dados, apenas o SQLDataSet onde o mesmo não sofre quaisquer tipo de manipulação.

A maquina onde esta sendo gerado possui 16 Gb de memória e é possivel verificar o valor  em memória da aplicação chegando a 1704 Mb, no momento em que ocorre a mensagem.

I250.png

Link para o comentário
Compartilhar em outros sites

Atualmente usamos a opção ReportMemoryLeaksOnShutdown.

 

An unexpected memory leak has occurred. The unexpected small block leaks are:

1 - 12 bytes: Unknown x 47
13 - 20 bytes: UnicodeString x 6, Unknown x 11
21 - 28 bytes: UnicodeString x 1, Unknown x 2
29 - 36 bytes: UnicodeString x 3
37 - 44 bytes: UnicodeString x 3, Unknown x 3
69 - 76 bytes: UnicodeString x 1
85 - 92 bytes: TStringList x 2, UnicodeString x 1
93 - 100 bytes: Unknown x 2
109 - 116 bytes: TDados_Empresa x 1
117 - 124 bytes: Unknown x 3
133 - 140 bytes: Unknown x 2
253 - 268 bytes: Unknown x 7
301 - 316 bytes: Unknown x 6
477 - 524 bytes: Unknown x 1
957 - 1052 bytes: Unknown x 1

Na geração do arquivo, dividimos o periodo em intervalos menores e o o ano completo foi gerado, sem nenhum tipo de exceção

Link para o comentário
Compartilhar em outros sites

  • Moderadores
20 horas atrás, Marcos JasKow disse:

Não consegui localizar

teria que validar seu código e verificar os possíveis erros e vazamentos.

 

Consultor SAC ACBr Juliomar Marchetti
 

Projeto ACBr

skype: juliomar
telegram: juliomar
e-mail: [email protected]
http://www.juliomarmarchetti.com.br
MVP_NewLogo_100x100_Black-02.png
 

 

Link para o comentário
Compartilhar em outros sites

Verificamos os pontos de vazamento de memória e mesmo assim prossegue.

Realizamos algumas verificações, isolamos o campo Histórico do registro I250 e gerou o arquivo completo com os 6,5 milhões de linhas no arquivo. Estamos buscando metodos de preenchimento deste campo,

para que somente no momento em que for realizar a gravação do arquivo o mesmo seja atribuido.

 

Link para o comentário
Compartilhar em outros sites

  • 1 mês depois ...

Ja tive esse problema a um ano atras, também com um cliente que tinha uma quantidade massiva de informações (4gb+, 40mi+ de linhas). No meu caso, alem do bloco I dar problema, a rotina TotalizarTermos também não funcionava, e tive que modificar a mesma. Segue em anexo as minhas modificações.

Essas modificações requerem que você também mexa na unit que gera essas informações pelo ACBrSPED. Não consegui pensar numa forma de que tudo isso ficasse dentro do componente sem precisar ter alterações externas.

- Fazer a classe "hack" da TACBrSPEDContabil

Na unit que gera os dados do sped, é necessario ter um type sobre o proprio componente do TACBrSPEDContabil. Serve pra utilizar as rotinas que não estão no public e que eu, propositalmente, não coloquei la, pois não queria que a classe começasse a ficar com muito "lixo" de informação.

TACBrSPEDContabilHack = class(TACBrSPEDContabil);

- Bloco I precisa ter seus registros chamados manualmente

Não utilizaremos mais a rotina TACBrSPEDContabil.WriteBloco_I para escrever nosso Bloco I. Basicamente, para cada chamada que existe na rotina WriteBloco_I, ela sera feita logo depois de adicionar os registros. Exemplo:

with ComponenteSPEDContabil.Bloco_I do
begin
  ...

  DataSet.First;
  while not(DataSet.Eof) do
  begin
    with RegistroI050.New do
    begin
      ...

      with RegistroI051.New do
      begin
        ...
      end;

      with RegistroI052.New do
      begin
        ...
      end;
    end;

    DataSet.Next;
  end;
end;

TACBrSPEDContabilHack(ComponenteSPEDContabil).WriteRegistroI050;
ComponenteSPEDContabil.Bloco_I.RegistroI050.Clear;

Esse metodo de adicionar todos os registros necessarios e escrever depois funcionou em todos os registros, exceto no I200 e I250, que são os mais pesados, no meu caso.

- Registros I200 e I250, casos especiais

Esses dois registros são os responsaveis pelo qual optei por fazer o arquivo ir salvando conforme vai recebendo dados, pois o computador não aguentava tudo em memória.

Na rotina que gera essas linhas, são necessários 3 variaveis pra ela funcionar:

i_200_9900, i_250_9900: Integer;
Reg200: TRegistroI200;

Conforme as modificações na unit do ABCrSpedContabil, a rotina WriteRegistroI200 agora pode receber 2 parametros (integer) de entrada, os quais vão guardar o index do registro 9900 para o registro I200 e I250. Como há muito registro nesse bloco, é necessário fazer com que ele vá salvando os registros I200 conforme for preciso, ao inves de adicionar tudo e fazer no final, como os outros blocos, e essas duas variaveis integer, vão salvar pra nós o index do bloco 9 que correspondem aos registros I200 e I250.

A variavel Reg200 serve pra dizer quando é preciso criar um novo registro. No meu caso, os registros I200 só são criados quando há uma troca em uma das seguintes propriedades dele: NUM_LCTO ou DT_LCTO ou IND_LCTO. Quando isso ocorre, eu sei que ele vai trocar de I200, então é nesse momento que eu faço ele salvar no txt os dados que contêm na variavel.

Código exemplo de toda essa situação:

Reg200 := nil;

i_200_990 := -1;
i_250_9900 := -1;

with ComponenteSPEDContabil.Bloco_I do
begin
  ...

  DataSet.First;
  while not(DataSet.Eof) do
  begin
    if not(Assigned(Reg200)) or ((Reg200.NUM_LCTO <> DataSet['EMP'] +'-'+ DataSet['LOTE'] +'-'+ DataSet['DATA']) or
                                 (Reg200.DT_LCTO <> DataSet['DATA']) or
                                 (Reg200.IND_LCTO <> DataSet['TIPO'])) then
    begin
      if Assigned(Reg200) then
      begin
        TACBrSPEDContabilHack(ComponenteSPEDContabil).WriteRegistroI200(i_200_990, i_250_9900);
        ComponenteSPEDContabil.Bloco_I.RegistroI200.Clear;
        ComponenteSPEDContabil.Bloco_I.RegistroI250Count := 0;
      end;

      Reg200 := RegistroI200.New;
      Reg200.NUM_LCTO := DataSet['EMP'] +'-'+ DataSet['LOTE'] +'-'+ DataSet['DATA'];
      Reg200.DT_LCTO := DataSet['DATA'];
      Reg200.IND_LCTO := DataSet['TIPO'];

      ....
    end;

    with Reg200.RegistroI250.New do
    begin
      ...
    end;

    with Reg200.RegistroI250.New do
    begin
      ...
    end;

    DataSet.Next;
  end
end;

TACBrSPEDContabilHack(ComponenteSPEDContabil).WriteRegistroI200(i200, i250);
ComponenteSPEDContabil.Bloco_I.RegistroI200.Clear;
ComponenteSPEDContabil.Bloco_I.RegistroI250Count := 0;

Outro caso especial nesses registro é que é preciso limpar a variavel RegistroI250Count, pois ela é um contador dos I250 que são adicionados num I200, então mesmo que você de um Clear nos I200, essa variavel ja foi incrementada durante o .New do I250, então tem que limpar ela.

- Finalizando o Bloco I

Apos isso, a unica coisa que resta é chamar as rotinas que vão finalizar o nosso Bloco I. São elas:

TACBrSPEDContabilHack(ComponenteSPEDContabli).WriteRegistroI990;

ComponenteSPEDContabli.Bloco_I.WriteBuffer;
ComponenteSPEDContabli.Bloco_I.Conteudo.Clear;
ComponenteSPEDContabli.Bloco_I.Gravado := True;

- TotalizarTermos

Depois que todos os blocos foram gerados, é necessário chamar a rotina que vai trocar as tags [**] pelo número de registros do componente. A chamada dela é simples, mas ela deve ser a ultima coisa que a sua rotina chama:

TACBrSPEDContabilHack(ComponenteSPEDContabil).TotalizarTermos;

A mudança que eu fiz nela foi devido ao arquivo ser muito grande, ele também estava dando Out of memory, então após algumas pesquisas na internet, me deparei com uma rotina que funcionou perfeitamente para o meu caso.

-

Como disse antes, não consegui pensar num jeito bom de implementar isso no componente do ACBr, pois acredito que o jeito mais certo seria ter um propriedade (provavelmente boolean) que indicasse que tu queria fazer o arquivo ser salvo por registro. Como estava com pouco tempo por agora, e eu ja sabia que esse metodo funcionava, optei por so usar ele mesmo e compartilhar com a comunidade pra ver se alguem não tem alguma ideia que possa ser implementa no componente.

Tentei resumir um pouco da situação toda e dos codigos, então se alguem ainda tiver duvidas, estou disposto a ajudar.

ACBrECDBloco_9.pas ACBrSpedContabil.pas

Editado por geovanesilveira
Link para o comentário
Compartilhar em outros sites

  • 11 meses depois ...
  • Este tópico foi criado há 339 dias atrás.
  • Talvez seja melhor você criar um NOVO TÓPICO do que postar uma resposta aqui.
Visitante
Este tópico está agora fechado para novas respostas
×
×
  • Criar Novo...

Informação Importante

Colocamos cookies em seu dispositivo para ajudar a tornar este site melhor. Você pode ajustar suas configurações de cookies, caso contrário, assumiremos que você está bem para continuar.